2010-06-14 108 views
17

我是從一本書閱讀SIOF它舉了一個例子:靜態初始化順序的悲劇

//file1.cpp 
extern int y; 
int x=y+1; 

//file2.cpp 
extern int x; 
int y=x+1; 

現在我的問題是:
在上面的代碼中,將下面的事情發生嗎?

  1. 編譯file1.cpp時,編譯器會保留y,因爲它不會爲它分配存儲空間。
  2. 編譯器爲x分配存儲空間,但不初始化它。
  3. 編譯file2.cpp時,編譯器會離開x,即不爲其分配存儲空間。
  4. 編譯器爲y分配存儲空間,但不初始化它。
  5. 在鏈接file1.o和file2.o時,現在先讓file2.o初始化,那麼現在:
    x是否獲取初始值0?或沒有被初始化?

回答

10

初始化在3.6.2 C++標準的「非本地對象的初始化」被給予步驟:

步驟1:xy是任何其它初始化發生之前初始化爲零。

步驟2:xy被動態初始化 - 哪一個是標準未指定的。該變量將得到值1,因爲其他變量將被初始化爲零。

第3步:其他變量將被動態初始化,得到值爲2

+0

謝謝..但在第1步中,當wll x和y將被初始化爲零: 在編譯時或鏈接時? – 2010-06-14 07:48:51

+6

@Happy Mittal:你無法分辨,因此編譯器可能會選擇。它甚至可能在程序加載時。 – MSalters 2010-06-14 11:35:01

1

它依賴於編譯器,可能依賴於運行時。當訪問文件中的第一個變量或訪問每個變量時,編譯器可能決定懶惰地初始化靜態變量。否則,它將在啓動時通過文件初始化所有靜態變量,順序通常取決於文件的鏈接順序。文件順序可以根據依賴關係或其他依賴於編譯器的影響而改變。

靜態變量通常被初始化爲零,除非它們有一個不變的初始化器。再次,這是編譯器依賴。所以當其他變量初始化時,其中一個變量可能爲零。但是,由於兩者都有初始化器,一些編譯器可能會使值不確定。

我認爲最可能的情況是:

  1. 空間分配給變量,都具有值爲0
  2. 一個變量,例如x,被初始化並設置爲值1 。
  3. 其他的,說Y,被初始化並設置爲2。

你總是可以運行它,看看。可能有些編譯器會生成進入無限循環的代碼。

+0

但我的疑問是,如果編譯器不爲extern int y分配存儲空間,那麼它如何將它初始化爲零? 或者當y初始化爲零時?在編譯時或鏈接時? 初始化變量時是否有任何規則? – 2010-06-14 07:15:47

+0

你能解釋一步一步在編譯和鏈接時會發生什麼? – 2010-06-14 07:23:35

2

整點(以及它被稱爲「慘敗」的原因)是不可能肯定地說什麼會發生在這樣的情況下。從本質上講,你要求一些不可能的東西(兩個變量都比另一個大)。由於他們不能這樣做,他們會做的是接受一些問題 - 他們可能產生0/1,或1/0,或1/2,或2/1,或者可能(最好的情況)只是一個錯誤信息。

10

SIOF是一個非常多的運行時工件,編譯器和鏈接器與它沒有多大關係。考慮atexit()函數,它註冊要在程序退出時調用的函數。許多CRT實現對程序初始化有類似的東西,我們稱之爲atinit()。

初始化這些全局變量需要執行代碼,該值不能由編譯器確定。因此,編譯器會生成執行表達式並分配值的機器代碼片段。這些代碼片段需要在main()運行之前執行。

這就是atinit()發揮作用的地方。一個普通的CRT實現按順序遍歷atinit函數指針列表並執行初始化片段。問題是函數在atinit()列表中註冊的順序。雖然atexit()具有良好定義的LIFO順序,並且它由代碼調用atexit()的順序隱式確定,但atinit函數並非如此。語言規範不需要訂單,您可以在代碼中指定訂單。 SIOF是結果。

一個可能的實現是編譯器在單獨的節中發出函數指針。鏈接器將它們合併,生成atinit列表。如果你的編譯器這樣做,那麼初始化順序將由鏈接目標文件的順序決定。查看映射文件,如果編譯器執行此操作,您應該看到atinit部分。它不會被稱爲atinit,但可能會有一些帶有「init」的名稱。看一下調用main()的CRT源代碼也應該提供洞察力。