2010-10-20 51 views
3

下面的代碼片段:爲什麼我的記憶體佈局不同?

void (*foo)(); 
char X[1]; 
char Y[10]; 

可以intruitively給我一個可能的堆棧佈局:

gcc -S -o stack stack.c 

然後我:

| Y[10] | 
|---------| 
| X[1] | 
|---------| 
| foo | 
|---------| 

我通過生成使用ASM文件審查這呃,推動這些變量的順序是不同的。所以,如果我不小心做了一個X[1]我期待地址Y[0],但在實際佈局中,寫入X[1]的內容會覆蓋分配給foo的存儲器位置的第一個字節。重組是編譯器優化的一個步驟,還是有人能告訴我爲什麼會發生這種情況?

+0

@abelenky:哎呀...謝謝你注意到:)修正了它。 – Legend 2010-10-20 16:05:38

+1

請記住,如果編譯器發現它們是有益的,那麼它們甚至可能不會被放置在堆棧上,它們中的一些可能被存儲在寄存器中整個生命週期中。 – nos 2010-10-20 16:14:08

+0

限制變量內存佈局,你應該把它們打包在一個'結構'中。在你的例子中,編譯器只是按照聲明的順序將它們放到堆棧上,而堆棧則按照相反的順序被使用。 – valdo 2010-10-20 16:30:43

回答

4

你爲什麼說「應該」

當然,您建議的堆棧佈局應該是實現自動變量的一種特別的 - 非常明顯的方式的結果,但沒有什麼需要它。因此,沒有「應該」


給力的一些項目的順序在內存中,這樣就可以玩(行爲不確定,不安全完全不可移植和!)遊戲的方式覆蓋,使用struct和編譯器的填充#pragma秒。

+0

的事實,這是事實上的標準。自動變量存儲在堆棧中。 – Andrey 2010-10-20 16:11:54

+0

更改了我的句子。感謝您指出。 – Legend 2010-10-20 16:13:06

+0

@Audrey:絕對。但是並不要求在代碼中遇到空間。編譯器可以很容易地將它們堆疊在一個符號表中,直到它*有*爲它們騰出空間,然後通過名稱或任何其他邏輯打亂作者的幻想,以字母順序發出代碼預留空間。 – dmckee 2010-10-20 16:15:27

1

可能的猜測:編譯器會嘗試將char數組放在一起,以儘量減少插入的填充總量。

通常,CPU在某些「整體」位對齊上檢索多字節數據是最快樂的,這幾乎總是對應於機器的位寬。所以一個32字節的int將對齊到一個32位的邊界。爲了做到這一點,編譯器將用「永不訪問的字節」填充堆棧。

但是,當您一次檢索一個字節時,這樣的對齊沒有任何好處。

1
| var | address | 
|---------|---------| 
| Y[10] | x  | 
|---------|---------| 
| X[1] | x + 10 | 
|---------|---------| 
| foo | x + 11 | 
|---------|---------| 

堆棧增長降低的地址,所以如果訪問下一個地址(地址更高),狀陣列的下一個元素你在更大的地址訪問存儲器。所以X[1] = *(x + 10 + 1) = foo

+0

在* some *(剛剛流行的大多數流行的)架構上,堆棧向低地址發展。 – dmckee 2010-10-20 16:12:24

+0

@dmckee是的。這個答案在那種情況下是有效的,這也是這個問題是關於 – Andrey 2010-10-20 16:17:43

1

這是因爲大多數體系結構中的stack grows down

+0

我一直以爲DOWN意味着更高的地址 – Andrey 2010-10-20 16:17:07

+0

這取決於你如何看待你的記憶:)在這種情況下,「下」意味着更低的地址。 – 2010-10-20 16:23:38

1

堆棧在大多數平臺上增長下來,但爲什麼依賴它?編譯器優化可能也會將變量與4個字節邊界對齊。爲什麼不這樣做?

char x[11]; 
char *y = &x[1]; 
2

即使沒有優化,內存中變量的排序通常也不是您可以指望的。無論如何,它們最終的順序取決於你如何看待它們。如果你看到一羣人從最短到最高排列,他人可能會說這些人實際上是從最高到最短。

影響這些變量在內存中的順序的第一件事就是編譯器如何實現。它有一個列表,列表可以從頭到尾或從​​頭到尾進行處理。所以編譯器讀取你的代碼,產生中間代碼,這個中間代碼有一個需要放在堆棧上的局部變量列表。編譯器並不關心它們在代碼中的順序,所以它只是以最方便的順序查看它們。

第二件事是許多處理器使用一個顛倒的堆棧。如果你:

push A 
push B 

然後A有一個比B更大的地址,儘管B位於堆棧的頂部(並在A之上)。想象這樣的一個好方法是使用C數組:

int stk[BIG]; 
int stk_top = BIG; 

然後

void stk_push(int x) { 
    stk_top--; 
    stk[stk_top] = x; 
} 

正如你可以看到stk_top指數實際上縮小爲堆棧得到它更多的項目。

現在回到優化 - 編譯器在重新排序不在結構中的事物時非常自由。這意味着你的編譯器可以很好地對堆棧中的局部變量進行重新排序,並且在那裏添加額外的填充字節來保持對齊。另外,編譯器也可以自由地將一些局部變量放在堆棧中。僅僅因爲你命名了一個局部變量並不意味着編譯器必須在程序中真正生成它。如果一個變量沒有被實際使用,它可能會被排除在程序之外。如果一個變量被大量使用,它可能被保存在一個寄存器中。如果一個變量只用於程序的一部分,那麼它可能只是臨時存在,並且它所使用的內存可以在該函數期間在幾個其他臨時變量之間共享。

+1

編譯器實際上也可以自由地優化結構(它們可能不會重新排序,但它們肯定會填充它們)。他們都癡迷於調整事物。所以當你做sizeof(mystruct):)時你會很容易感到驚訝:) – Jaka 2010-10-20 16:56:23

相關問題