对函数调用栈的困惑

标签 c callstack

根据维基:

the caller pushes the return address onto the stack, and the called subroutine, when it finishes, pops the return address off the call stack and transfers control to that address.

图片来自维基:

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>  |

问题:

根据引用维基的话,

the called subroutine, when it finishes, pops the return address off the call stack and transfers control to that address.

1.我画的对吗?

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

pop the return address off the call stack and transfer control to that address

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

更新:

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

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

那么调用栈应该是什么样的呢?

最佳答案

您的绘图不正确。函数的局部堆栈变量都在任何返回地址之下。否则,正如您所观察到的,当您调用函数时,局部变量会丢失。

应该是这样的:

| 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>  |

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

关于对函数调用栈的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7493785/

相关文章:

c# - Visual Studio 2010 中的实时 CallStack

我们能否始终针对崩溃问题获得正确(或完整)的堆栈转储

javascript - 高效的调用堆栈中断技术

计算前缀表达式unix

c - 为什么这个函数序言中没有 "sub rsp"指令,为什么函数参数存储在负 rbp 偏移处?

c - C 中的链表

c - 除了将其复制到内存位置或将其定义为静态之外,是否有一种方法可以监视正在运行的实时软件上的局部变量?

c - 无分支溢出处理

delphi - 随时从EurekaLog获取调用栈

c - 使用金丝雀值的堆栈损坏检测