ely*_*hiv 82 c gcc memory-management
根据这部电影(约38分钟),如果我有两个具有相同本地变量的函数,它们将使用相同的空间.所以下面的程序应该打印出来5
.用gcc
结果编译它-1218960859
.为什么?
该程序:
#include <stdio.h>
void A()
{
int a;
printf("%i",a);
}
void B()
{
int a;
a = 5;
}
int main()
{
B();
A();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
根据要求,这是反汇编程序的输出:
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
Run Code Online (Sandbox Code Playgroud)
Jon*_*art 130
是的,是的,这是未定义的行为,因为您使用的是未初始化的变量1.
但是,在x86架构2上,此实验应该可行.该值不会从堆栈中"擦除",并且由于它未初始化B()
,因此只要堆栈帧相同,该值仍应存在.
我冒昧地猜测,因为内部int a
未使用void B()
,编译器优化了代码输出,并且从未将5写入堆栈中的该位置.尝试添加一个printf
in B()
- 它可能会工作.
此外,编译器标志 - 即优化级别 - 也可能影响该实验.尝试通过传递-O0
给gcc来禁用优化.
编辑:我刚用gcc -O0
(64位)编译你的代码,实际上,程序打印5,就像熟悉调用堆栈所期望的那样.事实上,它甚至没有工作-O0
.32位版本可能表现不同.
免责声明:千万不要,永远使用这样的"真正的"代码!
1 - 关于这是否是正式的"UB",或者只是不可预测,下面正在进行辩论.
2 - 也是x64,可能是使用调用堆栈的每个其他架构(至少有一个MMU)
让我们来看看一个原因,它并没有正常工作.这在32位中最好看,所以我将编译-m32
.
$ gcc --version
gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)
Run Code Online (Sandbox Code Playgroud)
我编译$ gcc -m32 -O0 test.c
(禁用优化).当我运行它时,它打印垃圾.
看$ 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
Run Code Online (Sandbox Code Playgroud)
我们看到,B
编译器保留了0x10字节的堆栈空间,并将int a
变量初始化[ebp-0x4]
为5.
在A
然而,编译器放置int a
在[ebp-0xc]
.所以在这种情况下,我们的局部变量并没有在同一个地方结束!通过添加printf()
在呼叫A
以及将导致堆栈帧为A
和B
是相同的,并且打印55
.
cyr*_*iel 12
要记住一件重要的事情 - 不要依赖类似的东西,永远不要在真正的代码中使用它!这只是一个有趣的事情(甚至并非总是如此),而不是一个特征或类似的东西.想象一下,你自己试图找到那种"特征"所产生的错误 - 噩梦.
顺便说一句.- C和C++充满了那种"功能",这里有关于它的大幻灯片:http: //www.slideshare.net/olvemaudal/deep-c因此,如果你想看到更多类似的"功能",了解什么是在引擎盖下以及它如何工作只是观看这个幻灯片 - 你不会后悔,我相信大多数经验丰富的c/c ++程序员都可以从中学到很多东西.
编译代码gcc -Wall filename.c
您将看到这些警告.
In function 'B':
11:9: warning: variable 'a' set but not used [-Wunused-but-set-variable]
In function 'A':
6:11: warning: 'a' is used uninitialized in this function [-Wuninitialized]
Run Code Online (Sandbox Code Playgroud)
在c中打印未初始化的变量导致未定义的行为.
第6.7.8节C99标准的初始化说
如果未显式初始化具有自动存储持续时间的对象,则其值不确定.如果未显式初始化具有静态存储持续时间的对象,则:
— if it has pointer type, it is initialized to a null pointer;
— if it has arithmetic type, it is initialized to (positive or unsigned) zero;
— if it is an aggregate, every member is initialized (recursively) according to these rules;
— if it is a union, the first named member is initialized (recursively) according to these rules.
Run Code Online (Sandbox Code Playgroud)
EDIT1
如@Jonathon Reinhart如果您通过使用-O
标志禁用优化,gcc-O0
那么您可能会得到输出5.
但这并不是一个好主意,永远不要在生产代码中使用它.
-Wuninitialized
这是一个有价值的警告您应该考虑这个警告您不应该禁用或跳过此警告,这会导致生产中的巨大损坏,例如在运行守护程序时导致崩溃.
EDIT2
Deep C幻灯片解释了为什么结果是5/garbage.从这些幻灯片中添加这些信息并进行微小修改,以使这个答案更有效.
案例1:没有优化
$ gcc -O0 file.c && ./a.out
5
Run Code Online (Sandbox Code Playgroud)
也许这个编译器有一个它重用的命名变量池.例如,变量a被使用并释放
B()
,然后当A()
需要整数名称时a
,它将获得变量将获得相同的内存位置.如果您在重命名变量B()
,比方说b
,那么我不认为你会得到5
.
案例2:优化
当优化器启动时可能会发生很多事情.在这种情况下,我猜测B()
可以跳过调用,因为它没有任何副作用.此外,如果A()
内联main()
,即没有函数调用,我不会感到惊讶.(但由于A
()
具有链接器可见性,因此必须仍然创建该函数的目标代码,以防另一个目标文件想要与该函数链接).无论如何,我怀疑如果您优化代码,打印的值将是其他内容.
gcc -O file.c && ./a.out
1606415608
Run Code Online (Sandbox Code Playgroud)
垃圾!
归档时间: |
|
查看次数: |
4062 次 |
最近记录: |