Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
329 views
in Technique[技术] by (71.8m points)

c - Conflict between a Stanford tutorial and GCC

According to this movie (around minute 38), if I have two functions with the same local vars, they will use the same space. So the following program, should print 5. Compiling it with gcc results -1218960859. why?

The program:

#include <stdio.h>

void A()
{
    int a;
    printf("%i",a);
}

void B()
{
    int a;
    a = 5;
}

int main()
{
    B();
    A();
    return 0;
}

as requested, here is the output from the disassembler:

0804840c <A>:
 804840c:   55                      push   ebp
 804840d:   89 e5                   mov    ebp,esp
 804840f:   83 ec 28                sub    esp,0x28
 8048412:   8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]
 8048415:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 8048419:   c7 04 24 e8 84 04 08    mov    DWORD PTR [esp],0x80484e8
 8048420:   e8 cb fe ff ff          call   80482f0 <printf@plt>
 8048425:   c9                      leave  
 8048426:   c3                      ret    

08048427 <B>:
 8048427:   55                      push   ebp
 8048428:   89 e5                   mov    ebp,esp
 804842a:   83 ec 10                sub    esp,0x10
 804842d:   c7 45 fc 05 00 00 00    mov    DWORD PTR [ebp-0x4],0x5
 8048434:   c9                      leave  
 8048435:   c3                      ret    

08048436 <main>:
 8048436:   55                      push   ebp
 8048437:   89 e5                   mov    ebp,esp
 8048439:   83 e4 f0                and    esp,0xfffffff0
 804843c:   e8 e6 ff ff ff          call   8048427 <B>
 8048441:   e8 c6 ff ff ff          call   804840c <A>
 8048446:   b8 00 00 00 00          mov    eax,0x0
 804844b:   c9                      leave  
 804844c:   c3                      ret    
 804844d:   66 90                   xchg   ax,ax
 804844f:   90                      nop
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Yes, yes, this is undefined behavior, because you're using the variable uninitialized1.

However, on the x86 architecture2, this experiment should work. The value isn't "erased" from the stack, and since it's not initialized in B(), that same value should still be there, provided the stack frames are identical.

I'd venture to guess that, since int a is not used inside of void B(), the compiler optimized that code out, and a 5 was never written to that location on the stack. Try adding a printf in B() as well - it just may work.

Also, compiler flags - namely optimization level - will likely affect this experiment as well. Try disabling optimizations by passing -O0 to gcc.

Edit: I just compiled your code with gcc -O0 (64-bit), and indeed, the program prints 5, as one familiar with the call stack would expect. In fact, it worked even without -O0. A 32-bit build may behave differently.

Disclaimer: Don't ever, ever use something like this in "real" code!

1 - There's a debate going on below about whether or not this is officially "UB", or just unpredictable.

2 - Also x64, and probably every other architecture that uses a call stack (at least ones with an MMU)


Let's take a look at a reason why it didn't work. This is best seen in 32 bit, so I will compile with -m32.

$ gcc --version
gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)

I compiled with $ gcc -m32 -O0 test.c (Optimizations disabled). When I run this, it prints garbage.

Looking at $ objdump -Mintel -d ./a.out:

080483ec <A>:
 80483ec:   55                      push   ebp
 80483ed:   89 e5                   mov    ebp,esp
 80483ef:   83 ec 28                sub    esp,0x28
 80483f2:   8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]
 80483f5:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 80483f9:   c7 04 24 c4 84 04 08    mov    DWORD PTR [esp],0x80484c4
 8048400:   e8 cb fe ff ff          call   80482d0 <printf@plt>
 8048405:   c9                      leave  
 8048406:   c3                      ret    

08048407 <B>:
 8048407:   55                      push   ebp
 8048408:   89 e5                   mov    ebp,esp
 804840a:   83 ec 10                sub    esp,0x10
 804840d:   c7 45 fc 05 00 00 00    mov    DWORD PTR [ebp-0x4],0x5
 8048414:   c9                      leave  
 8048415:   c3                      ret    

We see that in B, the compiler reserved 0x10 bytes of stack space, and initialized our int a variable at [ebp-0x4] to 5.

In A however, the compiler placed int a at [ebp-0xc]. So in this case our local variables did not end up at the same place! By adding a printf() call in A as well will cause the stack frames for A and B to be identical, and print 55.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...