【问题标题】:confusion about function call stack关于函数调用堆栈的困惑
【发布时间】:2011-11-21 13:38:31
【问题描述】:

根据维基:

调用者将返回地址压入栈中,被调用者 子程序,当它完成时,从调用中弹出返回地址 堆栈并将控制权转移到该地址。

图片来自维基:

我不太明白这个。 假设我有一个 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>  |

问题:

根据维基引用的话,

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

1.我的画对吗?

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

从调用堆栈中弹出返回地址并将控制权转移到 那个地址

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

更新:

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

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

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

【问题讨论】:

    标签: c callstack


    【解决方案1】:

    您的绘图不正确。函数的本地堆栈变量都位于任何返回地址之下。否则,正如您所观察到的,当您调用函数时,本地人会迷路。

    应该是这样的:

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

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

    【讨论】:

    • 是的,先生,我同意你的看法。但是在“spam()”中,“foo()”在“”之前被调用,对吧?那么我应该将“int c”放在调用堆栈中的哪个位置?还低于退货地址?
    • 局部变量的堆栈保留通常在函数执行开始时一次性完成。所以 a、b 和 c 都是在调用 foo 之前保留的堆栈空间。
    • 知道了,先生。如果我在 main 中调用 spam() 和 foo() 会怎样(参见更新)?调用堆栈会是什么样子?你的意思是一个函数的堆栈帧的大小是它的本地大小的总和?
    • David 是对的,因为您似乎将变量声明误解为按顺序执行的语句。任何一个函数调用只会在调用堆栈上压入一个堆栈帧。此堆栈帧将已经拥有函数所需的所有空间(由编译器定义)。每个堆栈帧都会有一个函数返回的返回地址,以及所有参数和局部变量的固定空间。
    • 我想我已经回答了这个问题。我不想对这个问题进行冗长的认真编辑以提出后续问题。但是,在您的更新中,您通过推送返回值、调用函数、弹出返回值并跳转到那里来调用垃圾邮件。然后你以完全相同的方式调用 foo 。最后,您更新的问题没有完全指定,因为调用堆栈在不同的执行阶段看起来会有所不同。
    猜你喜欢
    • 2020-10-11
    • 2021-03-30
    • 2018-04-26
    • 2015-04-08
    • 2020-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多