2011-09-21 159 views
8

根據維基:混淆函數調用棧

呼叫者將返回地址壓入堆棧,和被叫 子程序中,當它完成,彈出返回地址斷呼叫 堆棧和轉移控制到那個地址。

產品圖來自維基:

enter image description here

我不太明白這一點。 說我有一個C程序如下:

#include <stdio.h> 

int foo(int x) 
{ 
    return x+1; 
} 

void spam() 
{ 
    int a = 1; //local variable 
    int b = foo(a); //subroutine called 
    int c = b; //local variable 
} 

int main() 
{ 
    spam(); 
    return 0; 
} 

而且我覺得調用堆棧應該是這樣的一個圖如下:

<None> means none local variables or params 

     _| parameters for foo() <int x> |_ 
top | local of spam() <int c>  | 
^  | return address of foo()  |<---foo() called, when finishes, return here? 
|  | local of spam() <int b>  | 
bot | local of spam() <int a>  | 
     _| parameters for spam() <None> |_ 
     | locals of main() <None>  | 
     | return address of spam()  |<---spam() called, when finishes, return here? 
     | parameters for main() <None> | 

問:

根據所引用的話來自Wiki,

被調用的子程序,當它完成時,彈出返回地址off 調用堆棧並將控制權轉交給該地址。

1.我的繪圖是否正確?

2.如果是正確的,那麼當FOO()完成後,它會

中彈出調用棧和傳輸控制的返回地址 ,解決

,但如何它可以彈出返回地址嗎? 因爲當foo完成時,當前堆棧指針指向垃圾郵件的本地, 對不對?

UPDATE:

什麼,如果在main()看起來是這樣的:

int main() 
{ 
    spam(); 
    foo(); 
} 

然後調用堆棧應該是什麼樣子?

回答

14

您的繪圖不正確。函數的本地堆棧變量都在任何返回地址之下。否則,正如你所看到的,當你調用一個函數時,當地人會迷路。

它應該是這樣的:

| parameters for foo() <int x> | 
| return address of foo()  | 
| local of spam() <int c>  | 
| local of spam() <int b>  | 
| local of spam() <int a>  | 
| parameters for spam() <None> | 
| return address of spam()  | 
| locals of main() <None>  | 
| parameters for main() <None> | 

我覺得困惑的是,您認爲變量聲明被視爲報表和按順序執行。事實上,編譯器通常會分析一個函數來決定所有局部變量需要多少堆棧空間。然後它發出相應的代碼來調整堆棧指針,並在進入函數時進行調整。任何對其他函數的調用都可以在不干擾該函數的堆棧幀的情況下壓入堆棧。

+0

是的,先生,我同意你的意見。但在「垃圾郵件()」中,「foo()」在「」之前被調用,對吧?那麼我應該把「int c」放在調用堆棧中?還低於返回地址? – Alcott

+3

本地變量的堆棧保留通常在函數執行開始時一勞永逸地完成。所以a,b和c都是在foo被調用之前保留的棧空間。 –

+0

明白了,先生。如果我在main中調用spam()和foo()會怎麼樣(請參閱UPDATE)?調用堆棧的外觀如何?你的意思是一個函數的堆棧幀的大小是它的本地大小的總和? – Alcott