Uri
Uri

Reputation: 89859

Strange stack behavior in C

I'm worried that I am misunderstanding something about stack behavior in C.

Suppose that I have the following code:

int main (int argc, const char * argv[]) 
{
    int a = 20, b = 25;
    {
        int temp1;
        printf("&temp1 is %ld\n" , &temp1);
    }

    {
        int temp2;
        printf("&temp2 is %ld\n" , &temp2);
    }
    return 0;
}

Why am I not getting the same address in both printouts? I am getting that temp2 is one int away from temp1, as if temp1 was never recycled.

My expectation is for the stack to contain 20, and 25. Then have temp1 on top, then have it removed, then have temp2 on top, then have it removed.

I am using gcc on Mac OS X.

Note that I am using the -O0 flag for compiling without optimizations.

Tho those wondering about the background for this question: I am preparing teaching materials on C, and I am trying to show the students that they should not only avoid returning pointers to automatic variables from functions, but also to avoid taking the address of variables from nested blocks and dereferencing them outside. I was trying to demonstrate how this causes problems, and couldn't get the screenshot.

Upvotes: 11

Views: 633

Answers (6)

sigjuice
sigjuice

Reputation: 29797

I believe the C standard just talks about the scope and lifetime of variables defined in a block. It makes no promises about how the variables interact with the stack or if a stack even exists.

Upvotes: 4

Juliano
Juliano

Reputation: 41475

There is no standard that sets how variables are placed on the stack. What happens in the compiler is much more complicated. In your code, the compiler may even choose to completely ignore and suppress variables a and b.

During the many stages of the compiler, the code may be converted to it's SSA form, and all stack variables lose their addresses and meanings in this form (it may even make it harder for the debugger).

Stack space is very cheap, in the sense that the time to allocate either 2 or 20 variables is constant. Also, stack space is very dynamic for most function calls, since with the exception of a few functions (those nearer main() and thread-entry functions, with long-lived event loops or so), they tend to complete quickly. So, you just don't bother with them.

Upvotes: 1

Alex B
Alex B

Reputation: 84972

I remember reading something about it. All I have now is this obscure link.

Just to let everybody know (and for the sake of the archives), it appears to be our kernel extension is running into a known limitation of GCC. Just to recap, we have a function in a very portable, very lightweight library, that for some reason is getting compiled with a 1600+ byte stack when compiled on/for Darwin. No matter what compiler options I tried, and what optimization levels I used, the stack was no smaller than 1400 "machine check" panic in pretty reproducible (but not frequent) situations.

After a lot of searching on the Web, learning some i386 assembly and talking to some people who are much better at assembly, I have learned that GCC is somewhat notorious for having horrid stack allocation. [...]

Apparently this is gcc's dirty little secret, except it's not much of a secret to some--Linus Torvalds has complained several times on various lists about the gcc stack allocation (search lkml.org for "gcc stack usage"). Once I knew what to search for, there was plenty of griping about gcc's subpar allocation of stack variables, and in particular, it's inability to re-use stack space for variables in different scopes.

With that said, my Linux version of gcc properly re-uses stack space, I get same address for both variables. Not sure what C standard says about it, but strict scope enforcement is only important for code correctness in C++ (due to destruction at the end of the scope), but not in C.

Upvotes: 2

Norman Ramsey
Norman Ramsey

Reputation: 202735

The compiler is completely within its rights not to optimize temp1 and temp2 into the same location. It has been many years since compilers generated code for one stack operation at a time; these days the whole stack frame is laid out at one go. (A few years back a colleague and I figured out a particularly clever way to do this.) Naive stack layout probably puts each variable in its own slot, even when, as in your example, their lifetimes don't overlap.

If you're curious, you might get different results with gcc -O1 or gcc -O2.

Upvotes: 19

Andrew Grant
Andrew Grant

Reputation: 58804

There is no guarantee what address stack objects will receive regardless of the order they are declared.

The compiler can happily reorder the creation and duration of stack variables providing it does not affect the results of the function.

Upvotes: 4

Daniel Earwicker
Daniel Earwicker

Reputation: 116764

This is completely dependent on the compiler and how it is configured.

Upvotes: 0

Related Questions