In C, do braces act as a stack frame?

10,858

Solution 1

No, braces do not act as a stack frame. In C, braces only denote a naming scope, but nothing gets destroyed nor is anything popped off the stack when control passes out of it.

As a programmer writing code, you can often think of it as if it is a stack frame. The identifiers declared within the braces are only accessible within the braces, so from a programmer's point of view, it is like they are pushed onto the stack as they are declared and then popped when the scope is exited. However, compilers don't have to generate code that pushes/pops anything on entry/exit (and generally, they don't).

Also note that local variables may not use any stack space at all: they could be held in CPU registers or in some other auxiliary storage location, or be optimized away entirely.

So, the d array, in theory, could consume memory for the entire function. However, the compiler may optimize it away, or share its memory with other local variables whose usage lifetimes do not overlap.

Solution 2

The time during which the variable is actually taking up memory is obviously compiler-dependent (and many compilers don't adjust the stack pointer when inner blocks are entered and exited within functions).

However, a closely related but possibly more interesting question is whether the program is allowed to access that inner object outside the inner scope (but within the containing function), ie:

void foo() {
   int c[100];
   int *p;

   {
       int d[200];
       p = d;
   }

   /* Can I access p[0] here? */

   return;
}

(In other words: is the compiler allowed to deallocate d, even if in practice most don't?).

The answer is that the compiler is allowed to deallocate d, and accessing p[0] where the comment indicates is undefined behaviour (the program is not allowed to access the inner object outside of the inner scope). The relevant part of the C standard is 6.2.4p5:

For such an object [one that has automatic storage duration] that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate. If an initialization is specified for the object, it is performed each time the declaration is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.

Solution 3

Your question is not clear enough to be answered unambiguously.

On the one hand, compilers don't normally do any local memory allocation-deallocation for nested block scopes. The local memory is normally allocated only once at function entry and released at function exit.

On the other hand, when the lifetime of a local object ends, the memory occupied by that object can be reused for another local object later. For example, in this code

void foo()
{
  {
    int d[100];
  }
  {
    double e[20];
  }
}

both arrays will usually occupy the same memory area, meaning that the total amount of the local storage needed by function foo is whatever is necessary for the largest of two arrays, not for both of them at the same time.

Whether the latter qualifies as d continuing to occupy memory till the end of function in the context of your question is for you to decide.

Solution 4

It's implementation dependent. I wrote a short program to test what gcc 4.3.4 does, and it allocates all of the stack space at once at the start of the function. You can examine the assembly that gcc produces using the -S flag.

Solution 5

No, d[] will not be on the stack for the remainder of routine. But alloca() is different.

Edit: Kristopher Johnson (and simon and Daniel) are right, and my initial response was wrong. With gcc 4.3.4.on CYGWIN, the code:

void foo(int[]);
void bar(void);
void foobar(int); 

void foobar(int flag) {
    if (flag) {
        int big[100000000];
        foo(big);
    }
    bar();
}

gives:

_foobar:
    pushl   %ebp
    movl    %esp, %ebp
    movl    $400000008, %eax
    call    __alloca
    cmpl    $0, 8(%ebp)
    je      L2
    leal    -400000000(%ebp), %eax
    movl    %eax, (%esp)
    call    _foo
L2:
    call    _bar
    leave
    ret

Live and learn! And a quick test seems to show that AndreyT is also correct about multiple allocations.

Added much later: The above test shows the gcc documentation is not quite right. For years it has said (emphasis added):

"The space for a variable-length array is deallocated as soon as the array name's scope ends."

Share:
10,858

Related videos on Youtube

Claudiu
Author by

Claudiu

Graduated from Brown University. E-mail: [email protected]

Updated on July 21, 2020

Comments

  • Claudiu
    Claudiu almost 4 years

    If I create a variable within a new set of curly braces, is that variable popped off the stack on the closing brace, or does it hang out until the end of the function? For example:

    void foo() {
       int c[100];
       {
           int d[200];
       }
       //code that takes a while
       return;
    }
    

    Will d be taking up memory during the code that takes a while section?

    • David Thornley
      David Thornley about 14 years
      Do you mean (1) according to the Standard, (2) universal practice among implementations, or (3) common practice among implementations?
  • avakar
    avakar about 14 years
    Isn't that implementation-specific?
  • kevinthompson
    kevinthompson about 14 years
    In C++, an object's destructor gets called at the end of its scope. Whether the memory gets reclaimed is an implementation-specific issue.
  • pm100
    pm100 about 14 years
    in c++ the closing brace will release any local variables. THis is a very common usage model for RAII
  • kenny
    kenny about 14 years
    So, I assume the answer to his second question is yes, the stack space for d[] is still in use.
  • Donal Fellows
    Donal Fellows about 14 years
    @pm100: The destructors will be called. That says nothing about the memory that those objects occupied.
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten about 14 years
    No guarantees. Once the scope closes the compiler isn't keeping track of that memory anymore (or at least is not required to...) and may well reuse it. This is why touching the memory formerly occupied by a out of scope variable is undefined behavior. Beware of nasal demons and similar warnings.
  • Thomas Matthews
    Thomas Matthews about 14 years
    How is d[] accessed outside of the local scope if it is still in existence (in use)? My understanding is that all variables declared in a scope block are destroyed (or made unavailable) when execution exits the scope (static variables do not conform to this rule).
  • caf
    caf about 14 years
    @Thomas: Easy - a pointer to a member of d[] could be stored in a function-scope pointer. The question I suppose is "is this allowed"?
  • caf
    caf about 14 years
    The C standard specifies that the lifetime of automatic variables declared in the block extends only until the execution of the block ends. So essentially those automatic variables do get "destroyed" at the end of the block.
  • kevinthompson
    kevinthompson almost 13 years
    Automatic variables are "destroyed" at the end of a block only in the sense that they are no longer usable and thus, in some sense no longer exist. But nothing "destroys" them (that is, there are no destructors called, no memory is deallocated, they don't get overwritten with zeroes, etc.).
  • george
    george about 12 years
    "If curly braces caused a stack push/pop, then the above code would not compile because the code inside the braces would not be able to access the variable var that lives outside the braces" - this is simply not true. The compiler can always remember the distance from the stack/frame pointer, and use it to reference outer variables. Also, see Joseph's answer for an example of curly braces that do cause a stack push/pop.
  • bta
    bta about 12 years
    @george- The behavior you describe, as well as Joseph's example, is dependent on the compiler and platform that you are using. For example, compiling the same code for a MIPS target yields completely different results. I was speaking purely from the point of view of the C spec (since the OP didn't specify a compiler or target). I'll edit the answer and add more specifics.
  • supercat
    supercat over 10 years
    @KristopherJohnson: If a method had two separate blocks, each of which declared a 1Kbyte array, and a third block which called a nested method, a compiler would be free to use the same memory for both arrays, and/or to place the array at the shallowest part of the stack and move the stack pointer above it calling the nested method. Such behavior could reduce by 2K the stack depth required for the function call.
  • Leushenko
    Leushenko over 8 years
    @KristopherJohnson that's a big assumption. There's no rule at all that says C has to actually use a stack for local variable memory - it could be allocating local objects dynamically and freeing them again at end of scope a bit like C++'s RAII. This would actually be a viable strategy for large VLAs (especially since the best C compilers usually have access to C++ codegen under the hood), so don't assume that "destroyed" is completely meaningless.
  • underscore_d
    underscore_d almost 8 years
    @KristopherJohnson Were you talking about C++? I guess not ;-) but if so, destructors are called when the enclosing block ends, not held until the end of some larger outer block. Otherwise new blocks wouldn't be such a common idiom for RAII in cases like locks, etc.
  • Peter Cordes
    Peter Cordes almost 7 years
    Compiling with optimization disabled doesn't necessarily show you what you'll get in optimized code. In this case, the behaviour is the same (allocate at the start of the function, and only free when leaving the function): godbolt.org/g/M112AQ. But non-cygwin gcc doesn't call an alloca function. I'm really surprised that cygwin gcc would do that. It's not even a variable-length array, so IDK why you bring that up.
  • Chris
    Chris about 6 years
    As someone learning how scope and memory works in C and C++ after years of using higher level languages, I find this answer more precise and useful than the accepted one.
  • rustyx
    rustyx over 3 years
    This answer is potentially misleading. Yes, there's often just one stack frame per function, but it contains a superposition of all nested scopes, meaning the compiler re-uses memory between scopes (example).