2011-09-21 128 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