关于函数调用堆栈的混淆

Alc*_*ott 10 c callstack

根据维基:

主叫方将返回地址压入堆栈,并且调用的子程序,当它完成,弹出返回地址关闭调用堆栈,并将控制转移到该地址.

来自Wiki的图片:

在此输入图像描述

我不太明白这一点.说我有一个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;
}
Run Code Online (Sandbox Code Playgroud)

我认为调用堆栈应该类似于绘图,如下所示:

<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>  |
Run Code Online (Sandbox Code Playgroud)

题:

根据Wiki引用的字样,

被调用的子例程,当它完成时,从调用堆栈中弹出返回地址并将控制转移到该地址.

我的抽签是对的吗?

2.如果它是正确的,那么当foo()完成时,它会

弹出调用堆栈的返回地址并将控制权转移到该地址

,但它如何弹出退货地址?因为当foo完成时,当前的堆栈指针指向垃圾邮件的本地,对吧?

更新:

如果main()看起来像这样:

int main()
{ 
    spam();
    foo();
}
Run Code Online (Sandbox Code Playgroud)

然后调用堆栈应该是什么样的?

Dav*_*nan 15

你的图纸不正确.函数的本地堆栈变量都低于任何返回地址.否则,正如您所观察到的,当您调用函数时,本地人会迷失方向.

它应该是这样的:

| 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>  |
Run Code Online (Sandbox Code Playgroud)

我认为令人困惑的是,您认为变量声明被视为语句并按顺序执行.实际上,编译器通常会分析一个函数来决定所有局部变量需要多少堆栈空间.然后它发出代码来相应地调整堆栈指针,并在进入函数时进行调整.然后,对其他函数的任何调用都可以推入堆栈,而不会干扰此函数的堆栈帧.

  • 局部变量的堆栈保留通常在函数执行开始时一劳永逸地进行.所以a,b和c都是在调用foo之前保留的堆栈空间. (3认同)