[英]Conflict between a Stanford tutorial and GCC
根據這部電影(大約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;
}
根據要求,這是反匯編程序的輸出:
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
是的,是的,這是未定義的行為 ,因為您使用的是未初始化的變量1 。
但是,在x86架構2上 , 此實驗應該可行 。 該值不會從堆棧中“擦除”,並且由於它未在B()
初始化,因此如果堆棧幀相同,則仍應存在相同的值。
我斗膽猜測,由於int a
不內所用的void B()
編譯器優化的代碼出來,和一個5永遠不會寫入到堆棧上的該位置。 嘗試在B()
添加printf
- 它可能會起作用。
此外,編譯器標志 - 即優化級別 - 也可能影響該實驗。 嘗試通過將-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)
我用$ 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
我們看到在B
,編譯器保留了0x10字節的堆棧空間,並將我們的int a
初始化為[ebp-0x4]
int a
變量為5。
但是,在A
中,編譯器在[ebp-0xc]
處放置int a
。 所以在這種情況下,我們的局部變量並沒有在同一個地方結束! 通過在A
添加printf()
調用也會導致A
和B
的堆棧幀相同,並打印55
。
這是未定義的行為 。 未初始化的局部變量具有不確定的值,使用它將導致未定義的行為。
要記住一件重要的事情 - 不要依賴類似的東西, 永遠不要在真正的代碼中使用它! 這只是一個有趣的事情(甚至並非總是如此),而不是一個特征或類似的東西。 想象一下,你自己試圖找到那種“特征”所產生的錯誤 - 噩夢。
順便說一句。 - C和C ++充滿了那種“功能”,這里有關於它的大幻燈片: http : //www.slideshare.net/olvemaudal/deep-c因此,如果你想看到更多類似的“功能”,了解什么是在引擎蓋下以及它如何工作只是觀看這個幻燈片 - 你不會后悔,我相信大多數經驗豐富的c / c ++程序員都可以從中學到很多東西。
在函數A
,變量a
未初始化,打印其值會導致未定義的行為。
在一些編譯器中,變量a
中A
和a
中B
在同一個地址,這樣就可以打印5
,但同樣,你不能依賴未定義行為。
使用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]
在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.
EDIT1
如@Jonathon Reinhart如果通過使用-O
標志gcc-O0
禁用優化,則可能會得到輸出5。
但這並不是一個好主意,永遠不要在生產代碼中使用它。
-Wuninitialized
這是一個有價值的警告你應該考慮這一個你不應該禁用或跳過這個警告,導致生產中的巨大損害,如在運行守護進程時導致崩潰。
EDIT2
Deep C幻燈片解釋了為什么結果是5 / garbage.從這些幻燈片中添加這些信息並進行微小修改,以使這個答案更有效。
案例1:沒有優化
$ gcc -O0 file.c && ./a.out
5
也許這個編譯器有一個它重用的命名變量池。 例如變量,使用和釋放B()
則當A()
需要一個整數名稱a
它將得到變量會得到同樣的存儲器位置。 如果你將B()
的變量重命名為b
,那么我認為你不會得到5
。
案例2:優化
當優化器啟動時可能會發生很多事情。在這種情況下,我猜測可以跳過對B()
的調用,因為它沒有任何副作用。 另外,如果A()
在main()
內聯,即沒有函數調用,我也不會感到驚訝。 (但是由於A ()
具有鏈接器可見性,因此必須仍然創建該函數的目標代碼,以防另一個目標文件想要與該函數鏈接)。 無論如何,我懷疑如果您優化代碼,打印的值將是其他內容。
gcc -O file.c && ./a.out
1606415608
垃圾!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.