理解局部變量和全局變量的內存問題核心是理解編譯器在主函數和子函數調用執行過程中是如何管理分配內存的。
內存中數據區被分為動態數據區與靜態數據區。其中靜態數據區可以簡單理解為寫在main函數與其他函數外部的全局變量存儲的區域,程序運行時,編譯器為其在這個區域內分配內存,其生命周期貫穿整個程序執行過程。
這里我們主要講講動態數據區,動態數據區中主要分為heap與stack。假設下圖為內存區域,其中的堆區和棧區分別具有基地址;堆區和棧區的內存分配都是先從基地址開始分配,並在內存釋放后指針再次回到基地址。這里紅色邊框區域為堆區,藍色為棧區;其中主函數中的變量在堆區分配,而主函數中調用的函數內部的局部變量在棧區分配,其生命周期為整個子函數調用期。以下面最簡單的小程序的執行為例:
堆基地址 |
(棧基地址) |
void f(int c) { int e; int f; } int main() { int a=1; int b=2; f(a);
int d=3;
f(b); return 0; }
首先程序從主函數開始執行,執行語句int a;int b;此時編譯器在堆區依次為其分配內存。
b=2 |
堆基 a=1 |
f |
e |
棧基 c |
當第一次調用子函數f時, c,e,f依次進棧(圖中省略了棧頂指針);
當第一次子函數f調用后,棧區內存被釋放,相當於f,e,c依次出棧,棧頂指針回到基地址,而此時,堆區內存並未釋放,因為主函數還沒有結束。int d=3;執行之后:
d=3 |
b=2 |
堆基 a=1 |
棧基 |
此時棧區為空,當再次執行子函數f時,如同第一次一樣,編譯器再次在棧區為其變量分配內存,從而局部變量進棧。f調用結束,棧區內存釋放。在遞歸算法的執行過程中,函數不斷調用自身,編譯器為每個子函數開辟棧區空間,其實現類似於此。當主函數結束后,堆區的內存才被釋放。
大多數時候,編譯器在編譯時在內存中做了很多工作,我們不能從代碼本身了解內存分配,例如理解 i++ 語句與i=i+1的區別,乍一看一樣,無非是給i 加1,但內存中卻有本質的區別,i=i+1 執行過程中,編譯器首先會將i+1的值保存在一個臨時變量中,這個臨時變量編譯器自動申請,然后再把這個臨時變量賦值給i , 而i++則直接給i加1;
更多c與c++內存分配問題請參考 http://www.cnblogs.com/dolphin0520/archive/2011/04/04/2005061.html