Is the memory for a function's local variables always freed once the function exits?

This is a discussion on Is the memory for a function's local variables always freed once the function exits? within the C Programming forums, part of the General Programming Boards category; Let's say I have a function that sets a number of local variables like this: Code: void test_func(){ long a ...

  1. #1
    Registered User
    Join Date
    Jun 2009
    Posts
    97

    Is the memory for a function's local variables always freed once the function exits?

    Let's say I have a function that sets a number of local variables like this:

    Code:
    void test_func(){
    
    	long a = 1; //4 bytes
    	long b = 2; //4 bytes
    	long c = 3; //4 bytes
    	long d = 4; //4 bytes
    
    	char text[] = "Hello, this is some text!"; //26 bytes
    	
    	//Total memory usage: 42 bytes
    }
    Will the memory used by the function always be freed once it ends? Are there any circumstances under which the memory wouldn't be freed upon exit?

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,397
    Quote Originally Posted by synthetix
    Will the memory used by the function always be freed once it ends?
    If you're only talking about the local variables, yes...

    Quote Originally Posted by synthetix
    Are there any circumstances under which the memory wouldn't be freed upon exit?
    ... except for static local variables, whose lifetime ends when the process terminates. Also, if the function is inlined, then the variables that appear to have a lifetime limited to the scope of this function would actually have a longer lifetime, but to you it won't matter. Oh, and then of course variables may be reused, or eliminated, etc, by the compiler as a matter of optimisation, but again to you it won't matter (besides being a win).
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,675
    Quote Originally Posted by synthetix View Post
    Are there any circumstances under which the memory wouldn't be freed upon exit?
    Except of what the witch said,memory that is allocated by you directly (malloc,realloc,calloc etc) will not be freed when the function terminates,unless you free them with the free function.This is a repeated hazard for memory leaks by many programmers...

  4. #4
    Registered User
    Join Date
    Aug 2012
    Posts
    6
    Quote Originally Posted by synthetix View Post
    Let's say I have a function that sets a number of local variables like this:

    Code:
    void test_func(){
    
    	long a = 1; //4 bytes
    	long b = 2; //4 bytes
    	long c = 3; //4 bytes
    	long d = 4; //4 bytes
    
    	char text[] = "Hello, this is some text!"; //26 bytes
    	
    	//Total memory usage: 42 bytes
    }
    Will the memory used by the function always be freed once it ends? Are there any circumstances under which the memory wouldn't be freed upon exit?
    It's not required by the standard, I think, but in practice that is typically how it's implemented, except for maybe string literals, which are generally stored in a static memory area.

  5. #5
    Registered User
    Join Date
    Mar 2009
    Posts
    344
    Quote Originally Posted by synthetix View Post
    Let's say I have a function that sets a number of local variables like this:

    Code:
    void test_func(){
    
    	long a = 1; //4 bytes
    	long b = 2; //4 bytes
    	long c = 3; //4 bytes
    	long d = 4; //4 bytes
    
    	char text[] = "Hello, this is some text!"; //26 bytes
    	
    	//Total memory usage: 42 bytes
    }
    Will the memory used by the function always be freed once it ends? Are there any circumstances under which the memory wouldn't be freed upon exit?
    The compiler isn't guaranteed to allocate memory for them in the first place - they could be stored in registers, replaced by a constant in an opcode, or removed entirely if they are dead code - so there might not even be memory to free to begin with. Don't confuse an abstraction of how the end results end up being computed with what they compiler must actually do.

  6. #6
    Registered User
    Join Date
    Sep 2008
    Location
    Toronto, Canada
    Posts
    1,831
    Good points in previous post regarding compiler optimization. But in general such local variables are on the stack. The code (or operating system) does not waste time 'deleting' data that's no longer accessible by the C scoping rules. So in theory, the data on the stack remains untouched until a new function call uses up the stack. This could also occur during vectored interrupt routines - but my guess is on modern system a different stack is used for system interrupts.

    There is no concept such as 'used' or 'freed'. The stack is a 64K area that's always allocated... but that's on ancient machines anyway. I wouldn't be surprised if the stack is now switched dynamically as the operating system moves things around as it pleases.

  7. #7
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    383
    Quote Originally Posted by nonoob View Post
    There is no concept such as 'used' or 'freed'. The stack is a 64K area that's always allocated... but that's on ancient machines anyway. I wouldn't be surprised if the stack is now switched dynamically as the operating system moves things around as it pleases.
    Yup, modern operating systems typically shuffle the stack around in virtual memory, which means memory for the whole stack doesn't have to be allocated to real memory until it's actually used (and parts of the stack can be swapped out to secondary storage when it's not being used). One Linux machine I'm using has a default stack size soft limit of 10MB per process (there is no hard limit), which ought to be plenty of space for most programs.

    (On the other hand, a lot of ancient machines had only 64KB of memory or less. The 6502 CPU, used by the C64, Apple II, and other machines, has an 8-bit stack pointer which limits the stack to 256 bytes on those computers!)

  8. #8
    Registered User
    Join Date
    Jun 2005
    Posts
    6,193
    Quote Originally Posted by synthetix View Post
    Will the memory used by the function always be freed once it ends? Are there any circumstances under which the memory wouldn't be freed upon exit?
    What do you mean by "freed"?
    Right 98% of the time, and don't care about the other 3%.

  9. #9
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,046
    More discussions about stack size: default stack size on modern Linux?
    (In general SuSE/Debian/Ubuntu derived systems have 8MB limits, Red Hat/Scientific Linux/Fedora derivatives have 10MB. Usually. You can change it of course.)

    To summarize what everyone has said, local variables that go on the stack will automatically be freed by definition; the values may sit in memory for a while but you shouldn't access them, as the values might change unexpectedly. (The operating system can even free that memory page, since it's "free", and you could get a segfault.) Some types of variables (static variables, string literals) are normally stored in the data segment. They persist from when the program is loaded until it's unloaded from memory, so they're "freed" when the operating system removes your program from memory.

    It's dangerous to predict how much memory will be used on the stack: even without compiler optimizations that shift stuff around, there may be padding involved. Why don't we find out? Comments in blue.
    Code:
    $ uname -srm
    Linux 3.2.0-2-amd64 x86_64    (I'm using a 64-bit Linux system)
    $ cat code.c 
    void test_func(){
     
        long a = 1; //4 bytes
        long b = 2; //4 bytes
        long c = 3; //4 bytes
        long d = 4; //4 bytes
     
        char text[] = "Hello, this is some text!"; //26 bytes
         
        //Total memory usage: 42 bytes
    }
    int main() {
        test_func();    (added a simple main)
        return 0;
    }
    $ gcc -g code.c -o code    (compile with debugging info)
    $ gdb -q code    (start the debugger in quiet mode)
    Reading symbols from /tmp/code...done.
    (gdb) start
    Temporary breakpoint 1 at 0x4004ee: file code.c, line 13.
    Starting program: /tmp/code 
    
    Temporary breakpoint 1, main () at code.c:13
    13	    test_func();
    (gdb) s    (step into the function)
    test_func () at code.c:3
    3	    long a = 1; //4 bytes
    (gdb) p/x $esp
    $2 = 0xffffe320
    (gdb) p/x $ebp
    $3 = 0xffffe320    (stack pointer/esp and base pointer/ebp are the same, means no stack space is used yet)
    (gdb) l
    1	void test_func(){
    2	 
    3	    long a = 1; //4 bytes
    4	    long b = 2; //4 bytes
    5	    long c = 3; //4 bytes
    6	    long d = 4; //4 bytes
    7	 
    8	    char text[] = "Hello, this is some text!"; //26 bytes
    9	     
    10	    //Total memory usage: 42 bytes
    (gdb) n    (let's keep stepping)
    4	    long b = 2; //4 bytes
    (gdb) 
    5	    long c = 3; //4 bytes
    (gdb) 
    6	    long d = 4; //4 bytes
    (gdb) 
    8	    char text[] = "Hello, this is some text!"; //26 bytes
    (gdb) n
    11	}
    (gdb) p/x $esp
    $7 = 0xffffe320
    (gdb) p/x $ebp
    $8 = 0xffffe320    (still no stack space used??)
    (gdb) disass test_func    (let's look at the assembly)
    Dump of assembler code for function test_func:
       0x0000000000400494 <+0>:	push   %rbp
       0x0000000000400495 <+1>:	mov    %rsp,%rbp
       0x0000000000400498 <+4>:	movq   $0x1,-0x8(%rbp)
       0x00000000004004a0 <+12>:	movq   $0x2,-0x10(%rbp)
       0x00000000004004a8 <+20>:	movq   $0x3,-0x18(%rbp)
       0x00000000004004b0 <+28>:	movq   $0x4,-0x20(%rbp)
       0x00000000004004b8 <+36>:	movl   $0x6c6c6548,-0x40(%rbp)
       0x00000000004004bf <+43>:	movl   $0x74202c6f,-0x3c(%rbp)
       0x00000000004004c6 <+50>:	movl   $0x20736968,-0x38(%rbp)
       0x00000000004004cd <+57>:	movl   $0x73207369,-0x34(%rbp)
       0x00000000004004d4 <+64>:	movl   $0x20656d6f,-0x30(%rbp)
       0x00000000004004db <+71>:	movl   $0x74786574,-0x2c(%rbp)
       0x00000000004004e2 <+78>:	movw   $0x21,-0x28(%rbp)
    => 0x00000000004004e8 <+84>:	pop    %rbp
       0x00000000004004e9 <+85>:	retq   
    End of assembler dump.
    (gdb)
    Basically what happened is that on my 64-bit machine, the compiler decided that it had enough registers to store all of test_func's local variables in registers! So there was actually no stack space used by your variables. With optimizations enabled, the code turns into
    Code:
    (gdb) disass test_func
    Dump of assembler code for function test_func:
       0x00000000004004a0 <+0>:	repz retq 
    End of assembler dump.
    (gdb)
    Which as you might guess is assembly-speak for "do nothing".

    Moral of this: compilers have many different resources available to them and they're smarter than you give them credit for. You might think you're saving a byte or wasting some clock cycles or whatever, but a lot of the time it doesn't even matter. The compiler will figure it out.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  10. #10
    Registered User
    Join Date
    Jun 2005
    Posts
    6,193
    Quote Originally Posted by dwks View Post
    the values may sit in memory for a while but you shouldn't access them, as the values might change unexpectedly.
    That is why I asked for the definition of "freed". Depending on what "freed" is taken to mean, "values may sit in memory for a while" might mean that the memory is NOT freed.

    Local (auto) variables no longer exist as far as the program is concerned when they pass out of scope (eg in this case, the function returns). That is why accessing the memory location, in any manner, yields undefined behaviour according to the standard - anything that happens is allowed. Whether the memory used to represent the variables is actually released, put onto a queue for reuse, overwritten, or something else (any of which might comply with some meaning of "freed") is specific to the compiler and host system.
    Right 98% of the time, and don't care about the other 3%.

  11. #11
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,046
    I take "freed" to mean what it usually does: that whatever bookkeeping was being used to keep track of the memory is no longer tracking it. For example, when you dynamically allocate something with malloc(), then free() it, you might still be able to access the memory location, and the original value might still be there. Of course, it's also possible that the operating system (or glibc etc) notices that you're trying to access freed memory or unmapped memory or what have you... but in principle, the value is still sitting in memory in that "freed" variable. (I'm talking about Linux here and probably other OSes.)

    Same thing with stack variables. The bookkeeping in this case is the stack pointer and base pointer. When some variable is abandoned lower down in the stack memory (i.e. passes out of scope), the value is there, and it may still be accessible... but accessing it is undefined behaviour and sooner or later, the program will crash if it tries to do this.

    You are, of course, talking at a more abstract level about what the standard says, and that's useful too. But at the same time I think it's important to understand how some real operating systems do this so that (e.g.) you know what's going on when you accidentally access an out-of-scope variable, and it seems to work for the longest time. Anyway. Enough of that. You're quite right as far as the standard is concerned.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  12. #12
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,292
    Take "freed" to mean that it is not leaked, and is no longer owned by the thing that had owned it. That's as far as one needs to go into it.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 19
    Last Post: 09-08-2011, 07:56 AM
  2. function exits alone
    By xixonga in forum C Programming
    Replies: 2
    Last Post: 12-04-2010, 12:17 PM
  3. Memory freed at the end of function?
    By walkingbeard in forum C Programming
    Replies: 5
    Last Post: 12-02-2009, 11:26 PM
  4. Replies: 15
    Last Post: 01-01-2008, 01:34 AM
  5. Replies: 10
    Last Post: 07-14-2003, 05:30 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21