某天,王尼瑪寫了段C程序:
1 #include <stdio.h> 2 3 void input() 4 { 5 int i; 6 int array[20]; 7 for(i = 0; i < 20; i++) 8 { 9 array[i] = i; 10 } 11 } 12 13 void output() 14 { 15 int i; 16 int array[20]; 17 for(i = 0; i < 20; i++) 18 { 19 printf("%d\n", array[i]); 20 } 21 } 22 23 int main() 24 { 25 input(); 26 output(); 27 while(1){} 28 return 0; 29 }
這段代碼的目的很簡單,在input函數中定義了array[20]並賦值,在output函數中輸出,運行結果如下:

Nice Work!
But……在input()后來一發printf()呢?????
1 int main() 2 { 3 input(); 4 printf("any string"); 5 output(); 6 while(1){} 7 return 0; 8 }

其實,只要學過一段時間的C語言的童鞋就會發現,剛剛開始那倆函數里定義的array[20]就出問題了,這倆array壓根兒沒關系,如果遇到這樣的代碼,第一反應就是通過參數或者全局變量的方法,讓這倆array有關系。
But,問題來了……王尼瑪是個新手,他將兩個array定義成一樣的名字認為他們就是同一個數組,並且,他振振有詞的說,我之前的代碼是沒問題的,只加了個printf就出問題了,應該就是這里有問題了,怎么可能是定義array的問題?
尼瑪,這只是巧合而已,你的第一段程序就是錯的!
可我的輸出是正確的啊……
這……
==============================================分割線================================================
其實大家都知道,問題的根源是output和input函數中的數組array雖然同名,但卻不是同一個數組,只是碰巧將原先賦值的內存給輸出了而已,要解釋這個問題,就需要了解C語言在函數調用過程中,堆棧是如何變化的。首先必須明確一點也是非常重要的一點,棧是向下生長的,所謂向下生長是指從內存高地址->低地址的路徑延伸,那么就很明顯了,棧有棧底和棧頂,那么棧頂的地址要比棧底低。對x86體系的CPU而言,其中
---> 寄存器ebp(base pointer )可稱為“幀指針”或“基址指針”,其實語意是相同的。
---> 寄存器esp(stack pointer)可稱為“ 棧指針”。
要知道的是:
---> ebp 在未受改變之前始終指向棧幀的開始,也就是棧底,所以ebp的用途是在堆棧中尋址用的。
---> esp是會隨着數據的入棧和出棧移動的,也就是說,esp始終指向棧頂。
見下圖,假設函數A調用函數B,我們稱A函數為"調用者",B函數為“被調用者”則函數調用過程可以這么描述:
(1)先將調用者(A)的堆棧的基址(ebp)入棧,以保存之前任務的信息。
(2)然后將調用者(A)的棧頂指針(esp)的值賦給ebp,作為新的基址(即被調用者B的棧底)。
(3)然后在這個基址(被調用者B的棧底)上開辟(一般用sub指令)相應的空間用作被調用者B的棧空間。
(4)函數B返回后,從當前棧幀的ebp即恢復為調用者A的棧頂(esp),使棧頂恢復函數B被調用前的位置;然后調用者A再從恢復后的棧頂可彈出之前的ebp值(可以這么做是因為這個值在函數調用前一步被壓入堆棧)。這樣,ebp和esp就都恢復了調用函數B前的位置,也就是棧恢復函數B調用前的狀態。

回到之前的問題,由於input函數和output函數為各自的array數組分配的空間在內存中的地址恰好相同,所以可以順利輸出其內容;但是在調用printf函數以后,由於堆棧中一部分內容被修改了,所以輸出結果前半部分是正確的,后半部分是錯誤的。看到這里,相信有童鞋會試着運行這段代碼,如果使用Turbo C,恭喜你可以獲得相同的結果(上述結果在Turbo C測試截圖);如果使用Visual Studio XXXX,將得到如下結果:

這是怎么回事呢?查看了反匯編,發現在Debug版本中,為了方便調試,VS會將數組初始化為0xCCCCCCCC,而output函數中的array數組是剛剛定義的,所以被VS初始化位0xCCCCCCCC,轉換成unsigned int就是-858993460。

當然,在Release版本中,為了提高效率,是不會對數組進行這種默認初始化的操作,那么結果是什么樣的呢?

納尼?!如果VS不給數組初始化,得到的結果為毛和Turbo C不一樣啊……
既然這樣,只能再次借助反匯編了,見下圖。可以發現input函數沒有對應的匯編語句,也就是說,由於這貨啥都不干,被編譯器優化掉了。既然沒有對數組array賦值,那么輸出的自然是內存里原先亂七八糟的數據了。

至於GCC會得出什么結果,作為Windows黨,就不測試了,感興趣的童鞋可以調整編譯選項自己試試看
