2016-02-25 108 views
-3

以下片段有意訪問t[4]後面的下一個sizeof(int)字節,因此我知道在此處製作的錯誤。我只是做這個實驗來看看編譯器如何處理堆棧分配。有關堆棧分配的問題C

int t[5], i; 

for (i = 0; i <= 5; i++) { 
    t[i] = 0; 
} 

當執行在Windows上的代碼,使用GNU C編譯器,程序總是陷在一個無限循環的移植版本。我確信這隻會發生,因爲ti被依次分配到堆棧上,並且t[5]指向與i變量相同的地址。因此,在執行t[5] = 0時,程序實際上將i的值設置爲零。

但是,當使用不同版本的GNU C編譯器進行編譯時,我從來沒有得到無限循環。地址t[5]與地址i不一樣。

我的問題是,爲什麼這種不同的行爲?我知道你不應該假設任何關於這個結果的事情,但是不應該以相同的方式發生堆棧分配嗎?

我真正好奇的是編譯器如何管理這些堆棧分配。有沒有填充?訂單是否與源代碼一樣?顯然,這與C標準無關,實現之間或者甚至是同一編譯器的不同版本之間存在差異。我很好奇,在這個特殊情況下可能的結果和考慮是什麼。

+3

'C'和'C++'有區別。請不要用這兩個標記問題。 –

+9

「我認爲堆棧分配應該以相同的方式進行。」 - 你爲什麼這麼想?這完全是一個實現細節,不以任何方式由語言定義。 – BoBTFish

+3

爲什麼你認爲無限循環的原因是修改'i'的任務?編譯器知道'i <= 5'永遠不可能是真實的並且優化比較。 –

回答

4

另一個人說,爲什麼你從標準的角度來看這種行爲,我會說你的代碼編譯優化和執行後可能會發生什麼。

首先:循環可能會展開並執行6次:

t[0] = 0; 
t[1] = 0; 
t[2] = 0; 
t[3] = 0; 
t[4] = 0; 
t[5] = 0; 
i = 6; 

這是允許的優化,這是會發生什麼。更多:如果以後不使用i,則可以將其完全刪除。

第二:編譯器可能會保留i沒有做任何堆棧分配註冊。

第三:它可能會在堆棧上以任何順序放置變量。對於在內存中保存變量的順序沒有實際的要求(也在它們的本地)。

如何知道會發生什麼?看看生成的程序集。這是瞭解發生情況的唯一方法。

順便說一句:無限循環並不總是發生在Windows上。事實上,我無法逐字強制你的代碼是一個無限循環。

+0

謝謝!我沒有真正考慮過編譯器優化,或者堆棧中的順序不一定相同的事實。 –

3

訪問t[5]是一個未定義的行爲。最後一項是t[4]t[0],t[1],t[2],t[3],t[4])沒有t[5]

通過未定義的行爲,可能會發生任何事情。它可能會給出預期的結果或完全搞砸。

我的問題是,爲什麼這種不同的行爲?

正如我以前寫的,這是UB你不能指望什麼。即使您嘗試多次,也可能會在同一臺機器上獲得另一個結果。

+0

很明顯,但那不是我的問題...... –

+0

@ Garf365:C++標準無法解釋它,但肯定不是這種情況,* nothing * can。 –

+0

@BenjaminLindley是的,但爲什麼?爲什麼我應該知道如果我做出不確定的事情會發生什麼? –

9

您正在處理未定義的行爲。編譯器不需要按順序佈置自動變量(因爲它們出現在源代碼中)。其中一些可能在寄存器中,或者可能以不同的方式訂購,例如,如果較小的偏移量便宜。

這樣的要求只存在於一個結構的成員(成員之間有任意填充)。

是否有填充?

是的,編譯器會遵守每種類型的對齊要求並相應地放置這些變量。

訂單是否與源代碼始終相同?

不,但這是許多利用依賴的東西。緩衝區溢出可能會覆蓋相鄰的變量並危及整個程序的執行。