我一直在努力更深入地了解编译器是如何生成机器代码的,更具体地说,是GCC是如何处理堆栈的.在这样做的过程中,我一直在编写简单的C程序,将它们编译成汇编语言,并尽力理解结果.下面是一个简单的程序及其生成的输出:

asmtest.c:

void main() {
    char buffer[5];
}

asmtest.s:

pushl   %ebp
movl    %esp, %ebp
subl    $24, %esp
leave
ret

令我困惑的是,为什么要为堆栈分配24个字节.我知道,由于处理器如何寻址内存,堆栈必须以4的增量分配,但如果是这样,我们应该只将堆栈指针移动8字节,而不是24字节.作为参考,17字节的缓冲区会产生移动了40字节的堆栈指针,而完全没有缓冲区会移动堆栈指针8.1到16字节(含)之间的缓冲区移动ESP到24字节.

现在假设8字节是一个必要的常量(需要它做什么?),这意味着我们将以16字节为单位进行分配.为什么编译器会以这种方式对齐呢?我使用的是x86_64处理器,但即使是64位字也应该只需要8字节对齐.为什么会有这样的差异呢?

作为参考,我在运行10.5、GCC4.0.1且未启用优化的Mac上编译了这篇文章.

推荐答案

这是一个由-mpreferred-stack-boundary=n控制的gcc特性,编译器试图使堆栈上的项与2^n对齐.如果将n更改为2,它将只在堆栈上分配8个字节.n的默认值为4,即它将try 与16字节边界对齐.

为什么会有"默认"8字节,然后是24=8+16字节,是因为堆栈已经包含了8个字节用于leaveret,所以编译后的代码必须先将堆栈调整8个字节,以使其与2^4=16对齐.

C++相关问答推荐

CC crate 示例不会与C函数链接

从C函数调用asm函数时生成错误的BLX指令(STM32H753上的gcc)

编译SDL 2时缺少SDL_ttf

如何在IF语句中正确使用0.0

如果包含路径不存在,我的编译器可以被配置为出错吗?(GCC、MSVC)

将宏值传递给ARM链接器,该链接器将变量放置在特定位置

GCC创建应用于移动项的单独位掩码的目的是什么?

为什么memcpy进入缓冲区和指向缓冲区的指针工作相同?

解决S随机内存分配问题,实现跨进程高效数据共享

如何在STM8项目中导入STM8S/A标准外设库(ST VisualDeveloper)?

如何仅使用软件重新初始化STM32微控制器中的USB枚举?

无法访问共享目标文件内的共享指针

C语言中神秘的(我认为)缓冲区溢出

为什么WcrTomb只支持ASCII?

如何将大写/小写土耳其字母相互转换?

try 查找带有指针的数组的最小值和最大值

与外部SPI闪存通信时是否应禁用中断?

我编写这段代码是为了判断一个数字是质数、阿姆斯特朗还是完全数,但由于某种原因,当我使用大数时,它不会打印出来

Tcl_GetDoubleFromObj在列表的迭代中是一个缺点

在我的函数中实现va_arg的问题