1. 程式人生 > >區域性變數記憶體分配時間、靜態變數的初始化時間、常量摺疊

區域性變數記憶體分配時間、靜態變數的初始化時間、常量摺疊

記憶體分配時間

例1 區域性變數

void Test1()
{
    …………
    int buf[1024 * 1024];
    …………
}

在windows下,由於棧限制為1M,上面程式碼可能會棧溢位,因此通過單步除錯我們可以知道,區域性變數是在進入函式時分配棧記憶體。

編譯器在編譯的過程中,遇到函式呼叫時,會加入幾條彙編指令。這些彙編指令的作用是:
1 分配一段棧空間,用於存放被調函式的引數和區域性變數。
2 call被用函式。
3 當被調函式返回時釋放掉這段分配棧空間。

例2 全域性/靜態變數

class B
{
public:
    B(){cout << "class B construct"
<< endl;} ~B(){} }; class A { public: A(){cout << "class A construct" << endl;} ~A(){} static B b; } B A::b; void test() { static A a; } int main() { test(); test(); return 0; }

上面程式碼執行,main函式前,會呼叫B的建構函式,
test函式中第一次static A a時會呼叫一次A的建構函式

  • 對於C語言的全域性和靜態變數,不管是否被初始化,其記憶體空間都是全域性的;如果初始化,那麼初始化發生在任何程式碼執行之前,屬於編譯期初始化。由於內建變數無須資源釋放操作,僅需要回收記憶體空間,因此程式結束後全域性記憶體空間被一起回收,不存在變數依賴問題,沒有任何程式碼會再被執行!

  • C++引入了物件,這給全域性變數的管理帶領新的麻煩。C++的物件必須有建構函式生成,並最終執行析構操作。由於構造和析構並非分配記憶體那麼簡單,可以說相當複雜,因此何時執行全域性或靜態物件(C++)的構造和析構呢?這需要執行相關程式碼,無法在編譯期完成,因此C++標準規定:全域性或靜態物件當且僅當物件首次用到時才進行構造,並通過atexit()來管理物件的生命期,在程式結束之後(如呼叫exit,main),按FILO順序呼叫相應的析構操作!

  • 總結:
    全域性變數、檔案域的靜態變數和類的靜態成員變數在main執行之前的靜態初始化過程中分配記憶體並初始化;區域性靜態變數(一般為函式內的靜態變數)在第一次使用時分配記憶體並初始化。這裡的變數包含內建資料型別和自定義型別的物件。

關於全域性/靜態變數的記憶體分配問題,上面總結裡已經說的很詳細了。
但是我有兩點不同看法:

  1. 區域性靜態變數的記憶體分配應該和全域性變數一樣,在main函式之前分配好,在第一次呼叫的時候只是初始化。
  2. 如果註釋掉 B A::b(即不對b進行顯式初始化),結果就不會呼叫B的建構函式。因此對於類的靜態成員來說,static B b只是宣告,B A::b才是定義,而類中如果是B b則是宣告且定義。

例3 常量

小結

  1. 對於const全域性常量,如果初始值是字面值常量,一般會存放在常量表中,編譯器在編譯過程中就會直接使用常量表中的值來進行語句的操作。如果經過優化,可能會使得直接使用常而放棄儲存在常量表中。
  2. 對於編譯器沒有辦法處理的初始值常量,一般就需要分配到記憶體空間,然後等待執行時進行賦值,取值時候也是從記憶體空間中提取。
  3. 對於非static 的 const區域性變數(如函式中),由於其是區域性變數,符號表中將不存在其值,如果需要記憶體空間也是在棧中進行分配。根據編譯器的優化,以及const 變數初始值的不同來決定是否需要分配記憶體空間。
  4. 對於static 的 const區域性變數(如函式中),會根據編譯器的優化能力,以及初始值,來決定其是不進行儲存,還是直接儲存在常量符號表,或者是棧中等等均有可能。
  5. 對於第三點和第四點可以字跡嘗試驗證。