gpt4 book ai didi

c - C在调用函数激活记录时究竟将其实际使用多少堆栈空间?

转载 作者:太空宇宙 更新时间:2023-11-04 01:17:45 24 4
gpt4 key购买 nike

环境:Windows10上的gcc版本6.3.0(MinGW.org gcc-6.3.0-1)
我在命令行编译并运行代码。
这是我的代码:

#include <stdio.h>  
int func(void){
int c;
printf("stack top in func \t%p\n", &c);
return 1;
}
void main(void) {
int arr[0];
int i;
printf("stack top before func \t%p\n", &i);
i = func();
int j;
printf("stack top after func \t%p\n", &j);
return;
}

结果如下:
stack top before func   0061FF2C
stack top in func 0061FEFC
stack top after func 0061FF28

函数内堆栈顶部与函数外堆栈顶部之间的间隙大小为48字节。
然后我将arr的大小改为1,结果是:
stack top before func   0061FF28
stack top in func 0061FEFC
stack top after func 0061FF24

间隙刚刚缩小,堆栈顶部在函数中保持不变。间隙大小现在是44字节。
当“arr”的大小为3时,它停止收缩。
新的间隔大小是52字节。
这是一种记忆管理策略吗?
当它选择使用52个字节时可以使用44个字节,并且在编译时可以知道函数调用之前变量的大小,这有什么好处?

最佳答案

我认为您对堆栈和编译器的工作方式做出了一些毫无根据的假设。即:
变量是在你声明它们的时候分配的,
最后一个变量占据了堆栈的顶部,
变量只占用他们需要的空间,
这有一个明确而确定的答案。
以下是在C、gcc、x86平台中调用函数时的大致情况,无需优化:
参数(如果有)存储在寄存器和/或堆栈中。32位和64位、整数/指针、浮点数和不同大小的结构、参数数、vararg等的详细信息是不同的。
执行call指令,将返回地址推送到堆栈上(我认为,由于不同的原因,32位和64位都占用了8个字节),并将处理器重定向到新地址。
堆栈指针在按下原始值BP(4或8字节)后保存在BP寄存器中。
堆栈指针递减的字节数足以容纳所有局部变量。
一回来,
BP寄存器的值覆盖堆栈指针,自动否定步骤4。然后弹出BP的原始值。
执行ret指令,弹出返回地址并跳到那里。
应当指出,这绝不是普遍的,也不能保证。”“简单”功能可以优化为跳过步骤3、4和5。步骤4原则上可以多次发生。可以对堆栈指针执行额外的魔法,比如将它对齐到两个边界的特定功率(如SSE指令操作数的128倍),分配一个称为红色区域的函数,比如cc函数等等。许多例外和特殊情况都存在。更多细节将取决于gcc命令行参数,或每个发行版的内置默认值。其他编译器可能遵循稍有不同但兼容的约定。但我们还是坚持这个模式。
重要的是要注意的是,在步骤4中,所有的局部变量通常都被分配到一起,所取的大小可能是所需的总大小或更多。例如,根据惯例,编译器可以确保堆栈指针在任何一点都是16的倍数(这样函数本身就可以依赖于此),在这种情况下,它会舍入到最接近的倍数(也与步骤1到步骤3中所做的有关)。在该区域内,为局部变量分配地址(与BP或SP的偏移量),以满足其大小和对齐要求。
您的示例,特别是alloca中的代码,无法工作,因为编译器不会按照您的意愿分配main的空间,只有在从j返回之后。它与函数开头的farr一起发生,变量的顺序是未指定的,很可能是为了将它们最好地“打包”到可用的空间中,而int在32位或64位边界处获取地址。即使这样,如果把i的地址作为“stack top after func”,计算也会出错:最多只能是“stack top after func and allocation”。通常,“stack top after func”必须与C调用约定中的“stack top before func”相同。
为了更具体地了解你的职能,我建议:
编译后学习汇编。j上的工具非常适合这个:here's your code由x86-64中的gcc 8.2编译,如图所示。
堆栈指针应该减少16(第6行)加8(RBP@line 4的大小)加上第28行存储返回地址所需的godbolt.com,8为64位模式。
使用调试器:

(gdb) b 11
(gdb) b 4
(gdb) run
Starting program: [redacted]
stack top before func 0x7fffffffd2dc

Breakpoint 1, main () at a.c:11
11 i = func();
(gdb) print $rsp
$1 = (void *) 0x7fffffffd2d0
(gdb) c
Continuing.

Breakpoint 2, func () at a.c:4
4 printf("stack top in func \t%p\n", &c);
(gdb) print $rsp
$2 = (void *) 0x7fffffffd2b0

这里可以看到 call减少了0x20==32。

关于c - C在调用函数激活记录时究竟将其实际使用多少堆栈空间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53409162/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com