Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jquery-ui/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 关于函数调用堆栈的混淆_C_Callstack - Fatal编程技术网

C 关于函数调用堆栈的混淆

C 关于函数调用堆栈的混淆,c,callstack,C,Callstack,根据维基: 调用方将返回地址推送到堆栈上,调用 子例程完成后,从调用中弹出返回地址 堆栈并将控制转移到该地址 维基图片: 我不太明白这一点。 假设我有一个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 =

根据维基:

调用方将返回地址推送到堆栈上,调用 子例程完成后,从调用中弹出返回地址 堆栈并将控制转移到该地址

维基图片:

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

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

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

应该是这样的:

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>  |
| foo()的参数|
|foo()的返回地址|
|垃圾邮件的本地版本()|
|垃圾邮件的本地版本()|
|垃圾邮件的本地版本()|
|spam()的参数|
|垃圾邮件的返回地址()|
|main()|的本地人
|main()的参数|

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

是的,先生,我同意您的看法。但是在“spam()”中,“foo()”在“”之前被调用,对吗?那么我应该把“intc”放在调用堆栈的什么地方呢?也在返回地址下方?局部变量的堆栈保留通常在函数开始执行时一次性完成。所以在调用foo之前,a、b和c都是保留的堆栈空间。明白了,先生。如果我在main中调用spam()和foo()(请参阅更新),会怎么样?调用堆栈将是什么样子?你是说函数堆栈帧的大小是其局部大小的总和吗?David是对的,因为你似乎误解了变量声明是按顺序执行的语句。任何一个函数调用都只会在调用堆栈上推送一个堆栈帧。此堆栈帧已经具有函数所需的所有空间(由编译器定义)。当函数返回时,每个堆栈帧都有一个返回地址,所有参数和局部变量都有一个设置的空间。我想我已经回答了这个问题。我不想对这个问题进行长时间的认真编辑,以询问后续问题。但是,在更新中,您可以通过按下返回值、调用函数、弹出返回值并跳到那里来调用垃圾邮件。然后你用完全相同的方式呼叫foo。最后,您更新的问题没有完全指定,因为调用堆栈在不同的执行阶段看起来是不同的。