dfg
dfg

Reputation: 797

Why can't you free variables on the stack?

The languages in question are C/C++.

My prof said to free memory on the heap when your done using it, because otherwise you can end up with memory that can't be accessed. The problem with this is you might end up with all your memory used up and you are unable to access any of it.

Why doesn't the same concept apply to the stack? I understand that you can always access the memory you used on the stack, but if you keep creating new variables, you will eventually run out of space right? So why can't you free variables on the stack to make room for new variables like you can on the heap?

I get that the compiler frees variables on the stack, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope? If not, why not?

Upvotes: 7

Views: 4370

Answers (7)

user2287171
user2287171

Reputation:

The end of the closed "}" braces is where the stack "frees" its memory. So if I have:

{
    int a = 1;
    int b = 2;

    {
        int c = 3; // c gets "freed" at this "}" - the stack shrinks
                   // and c is no longer on the stack.
    }
}                  // a and b are "freed" from the stack at this last "}".

You can think of c as being "higher up" on the stack than "a" and "b", so c is getting popped off before them. Thus, every time you write a "}" symbol, you are effectively shrinking the stack and "freeing" data.

Upvotes: 6

AliciaBytes
AliciaBytes

Reputation: 7429

There are already nice answers but I think you might need some more clarification, so I'll try to make this a more detailed answer and also try to make it simple (if I manage to). If something isn't clear (which with me being not a native english speaker and having problems with formulating answers sometimes might be likely) just ask in the comments. Also gonna take the use the Variables vs Objects idea that Kerrek SB uses in his answer.

To make that clearer I consider Variables to be named Objects with an Object being something to store data within your program.

Variables on the stack got automatic storage duration they automatically get destroyed and reclaimed once their scope ends.

{
    std::string first_words = "Hello World!";

    // do some stuff here...

} // first_words goes out of scope and the memory gets reclaimed.

In this case first_words is a Variable (since it got its own name) which means it is also an Object.

Now what about the heap? Lets describe what you might consider being "something on the heap" as a Variable pointing to some memory location on the heap where an Object is located. Now these things got what's called dynamic storage duration.

{
    std::string * dirty = nullptr

    {

        std::string * ohh = new std::string{"I don't like this"}    // ohh is a std::string* and a Variable
                                                                    // The actual std::string is only an unnamed
                                                                    // Object on the heap.

        // do something here

        dirty = ohh; // dirty points to the same memory location as ohh does now.

    }   // ohh goes out of scope and gets destroyed since it is a Variable.
        // The actual std::string Object on the heap doesn't get destroyed

    std::cout << *dirty << std::endl;   // Will work since the std::string on the heap that dirty points to
                                        // is still there.

    delete dirty; // now the object being pointed to gets destroyed and the memory reclaimed

    dirty = nullptr; can still access dirty since it's still in its scope.

} // dirty goes out of scope and get destroyed.

As you can see objects don't adhere to scopes and you got to manually manage their memory. That's also a reason why "most" people prefer to use "wrappers" around it. See for example std::string which is a wrapper around a dynamic "String".

Now to clarify some of your questions:

  1. Why can't we destroy objects on the stack?

    Easy answer: Why would you want to?

    Detailed answer: It would be destroued by you and then destroyed again once it leaves the scope which isn't allowed. Also you should generally only have variables in your scope that you actually need for your computation and how would you destroy it if you actually need that variable to finish your computation? But if you really were to only need a variable for a small time within a computation you could just make a new smaller scope with { } so your variable gets automatically destroyed once it isn't needed anymore.

    Note: If you got a lot of variables that you only need for a small part of your computation it might be a hint that that part of the computation should be in its own function/scope.

  2. From your comments: Yeah I get that, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope?

    They don't. Objects on the heap got no scope, you can pass their address out of a function and it still persists. The pointer pointing to it can go out of scope and be destroyed but the Object on the heap still exists and you can't access it anymore (memory leak). That's also why it's called manual memory management and most people prefer wrappers around them so that it gets automatically destroyed when it isn't needed anymore. See std::string, std::vector as examples.

  3. From your comments: Also how can you run out of memory on a computer? An int takes up like 4 bytes, most computers have billions of bytes of memory... (excluding embedded systems)?

    Well, computer programs don't always just hold a few ints. Let me just answer with a little "fake" quote:

    640K [of computer memory] ought to be enough for anybody.

    But that isn't enough like we all should know. And how many memory is enough? I don't know but certainly not what we got now. There are many algorithms, problems and other stuff that need tons of memory. Just think about something like computer games. Could we make "bigger" games if we had more memory? Just think about it... You can always make something bigger with more resources so I don't think there's any limit where we can say it's enough.

Upvotes: 4

Evgeny Panasyuk
Evgeny Panasyuk

Reputation: 9199

So why can't you free variables on the stack to make room for new variables like you can on the heap?

All information that "stack allocator" knows is ESP which is pointer to the bottom of stack.

   N: used
 N-1: used
 N-2: used
 N-3: used <- **ESP**
 N-4: free
 N-5: free
 N-6: free
 ...

That makes "stack allocation" very efficient - just decrease ESP by the size of allocation, plus it is locality/cache-friendly.

If you would allow arbitrary deallocations, of different sizes - that will turn your "stack" into "heap", with all associated additional overhead - ESP would not be enough, because you have to remember which space is deallocated and which is not:

   N: used
 N-1: free
 N-2: free
 N-3: used
 N-4: free
 N-5: used
 N-6: free
 ...

Clearly - ESP is not more enough. And you also have to deal with fragmentation problems.

I get that the compiler frees variables on the stack, but thats at the end of the scope of the variable right. Doesn't it also free a variable on the heap at the end of its scope? If not, why not?

One of the reasons is that you don't always want that - sometimes you want to return allocated data to caller of your function, that data should outlive scope where it was created.

That said, if you really need scope-based lifetime management for "heap" allocated data (and most of time it is scope-based, indeed) - it is common practice in C++ to use wrappers around such data. One of examples is std::vector:

{
    std::vector<int> x(1024); // internally allocates array of 1024 ints on heap
    // use x
    // ...
} // at the end of the scope destructor of x is called automatically,
  // which does deallocation

Upvotes: 3

ScottMcP-MVP
ScottMcP-MVP

Reputation: 10425

The heap is managed by code: Deleting a heap allocation is done by calling the heap manager. The stack is managed by hardware. There is no manager to call.

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 477368

Dynamically allocated objects ("heap objects" in colloquial language) are never variables. Thus, they can never go out of scope. They don't live inside any scope. The only way you can handle them is via a pointer that you get at the time of allocation.

(The pointer is usually assigned to a variable, but that doesn't help.)

To repeat: Variables have scope; objects don't. But many objects are variables.

And to answer the question: You can only free objects, not variables.

Upvotes: 8

Zach Stark
Zach Stark

Reputation: 575

Local variables on the stack don't actually get freed. The registers pointing at the current stack are just moved back up and the stack "forgets" about them. And yes, you can occupy so much stack space that it overflows and the program crashes.
Variables on the heap do get freed automatically - by the operating system, when the program exits. If you do

int x;
for(x=0; x<=99999999; x++) {
  int* a = malloc(sizeof(int));
}

the value of a keeps getting overwritten and the place in the heap where a was stored is lost. This memory is NOT freed, because the program doesn't exit. This is called a "memory leak". Eventually, you will use up all the memory on the heap, and the program will crash.

Upvotes: 0

Mios
Mios

Reputation: 247

Read about function calls - each call pushes data and function address on the stack. Function pops data from stack and eventually pushes its result.

In general, stack is managed by OS, and yes - it can be depleted. Just try doing something like this:

int main(int argc, char **argv)
     {
     int table[1000000000];
     return 0;
     }

That should end quickly enough.

Upvotes: 0

Related Questions