程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 簡單分析C語言中函數調用過程

簡單分析C語言中函數調用過程

編輯:關於C語言

#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黨,就不測試了,感興趣的童鞋可以調整編譯選項自己試試看

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved