2010-08-15 64 views
2

非常快速的問題給你。當我在C中存儲一些自動變量時,asm輸出如下所示:MOV ESP+4,#25h,我只想知道爲什麼編譯器不能計算自己的地址ESP+4關於C編譯器asm輸出的另一個問題

我想到了這一點,我真的找不到原因。我的意思是,編譯器不知道esp值?它應該是。而當使用另一個目標文件時,這也不應該成爲問題,因爲變量可能只是由地址來表示,並在以後連接,當所有自動變量已知時,因此可以分配正確的地址。謝謝。

回答

3

不,它不能提前知道esp的值。例如,一個遞歸函數,即。一個自稱的函數。假設這樣的函數有幾個參數通過堆棧傳入。這意味着每個參數都會在堆棧上佔用一些空間,從而改變寄存器的值。

現在,當輸入函數時,esp的確切值將取決於函數先前調用過多少次,並且編譯器無法在編譯時知道這一點。如果你懷疑這一點,需要一個功能像這樣:

void foobar(int n) 
{ 
    if (rand() % n != 17) 
     foobar(n + 1); 
} 

有沒有辦法編譯器將提前足夠聰明弄清楚,如果該函數將再次調用自身。

如果編譯器想要提前確定esp,那麼它實際上必須爲esp的每個可能值創建一個函數版本。

上面的解釋只考慮了一個函數。在現實世界的場景中,一個程序有很多相互依賴的功能,這導致相當複雜的「調用圖」。這與(除其他之外)不可預知的程序邏輯一起意味着編譯器將不得不創建每個函數的大量版本,只是爲了優化esp--這顯然沒有意義。


P.S:現在別的東西。實際上根本不需要優化[esp+N],因爲它不應該比簡單的[esp]花費更多的CPU時間......至少在英特爾奔騰CPU上不會。你可以說他們已經包含了對這個和更復雜的場景的優化。如果您對英特爾CPU感興趣,我建議您查閱文檔,查找某種機器指令的字節,例如機器指令的字節MOD R/MSIBhere for the SIB bytehere或當然,在英特爾的官方CPU開發人員文檔。

+0

還有一個問題,如果您在整個程序中沒有使用遞歸函數調用,是否可以在不使用間接地址的情況下完成? – 2010-08-16 00:41:04

+0

_ @ b-gen-jack-o-neill:_它會讓事情變得更容易。假設一個函數'caller'調用你的目標函數'f'很明顯。然後,'esp'可以基於'caller'的假設'esp'值來計算,加上''caller''的局部變量和'f'的參數可能需要的空間。 (事實上​​可能並非如此簡單。)上面的遞歸函數本質上可以轉化爲* n * + 1幾乎相同的函數(每次迭代一次,其中* n *是函數自己調用的次數;除爲esp設定的值)。 – stakx 2010-08-16 07:34:02

+0

_ @ b-gen-jack-o-neill:_另請參閱添加的P.S.到我的答案。它解決了你是否需要「優化」[esp + N]'這一非常重要的問題。 – stakx 2010-08-16 07:44:35

3

不,編譯器在運行時不知道ESP的值 - 它是堆棧指針。每次調用函數時都可能有所不同。也許最簡單的例子是一個遞歸函數 - 每次它調用它自己時,堆棧會更深入一些,以適應新調用的局部變量。每個堆棧幀都有自己的局部變量,每個堆棧幀位於堆棧的不同位置,因此有自己的地址(通常在ESP中)。

1

這對於堆棧的工作方式來說確實相當重要。爲自己推理,想象一下如何實現遞歸函數。

2

堆棧指針無法在編譯時計算。舉一個簡單的例子,爲什麼這是不可能的,只需考慮遞歸函數:同一個變量對於每個調用都有不同的地址,但它總是與運行的代碼相同。

2

不,編譯器不會提前知道該值。在一些非常基本的程序中(從main到被稱爲任何其他特定函數只有一條可能的「路徑」),它可以可能,但我不知道一個編譯器試圖計算這個。如果你有任何遞歸,或者從一個以上的地方調用一個函數,堆棧指針將有不同的值,這取決於它從何處被調用。

在任何情況下都沒有多少意義 - 因爲堆棧指針被大量使用,所以大多數CPU被設計爲通過堆棧指針間接尋址非常有效。事實上,它通常比提供絕對地址更高效。