为什么我的缓冲区在堆栈上分配的内存比我要求的多?
Why does my buffer have more memory allocated on the stack than I asked for?
这是我的源代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 500
int main(int argc, char** argv)
{
if (argc != 2)
exit(1);
char str[MAX];
strcpy(str, argv[1]);
return 0;
}
我 disas
使用 gdb
组合 main
并得到以下结果:
Dump of assembler code for function main:
0x0000000000001145 <+0>: push %rbp
0x0000000000001146 <+1>: mov %rsp,%rbp
0x0000000000001149 <+4>: sub [=11=]x210,%rsp
.
.
.
End of assembler dump.
这里值得注意的是:
0x0000000000001149 <+4>: sub [=15=]x210,%rsp
我的问题是-
为什么有 [=16=]x210
(528 字节),而我要求的应该是 [=17=]x1f4
(500 字节)?
我猜你正在使用 gcc 并在没有优化的情况下进行编译,比如 this (godbolt)。
这里发生了几件事:
首先,在没有优化的情况下进行编译时,编译器会尝试确保每个局部变量在内存中都有一个地址,以便调试器可以轻松地检查或修改它。这包括函数参数,这些参数在 x86-64 上以其他方式通过寄存器传递。因此,编译器需要分配额外的堆栈 space,其中 argc
和 argv
参数可以“溢出”。您可以在程序集的第 5 行和第 6 行看到溢出:
movl %edi, -516(%rbp)
movq %rsi, -528(%rbp)
如果仔细观察,您可能会注意到编译器将 argc
(来自 %edi
)放置在地址 -516(%rbp)
处浪费了 4 个字节,而 -520(%rbp)
本来可用.原因还不完全清楚,但毕竟不是优化!这样我们就达到了 516 字节。
另一个问题是 x86-64 ABI 需要 16 字节堆栈对齐;参见 。在这种情况下,长话短说,这意味着我们的堆栈调整需要是 16 字节的倍数。 (return 地址和压入的 rbp
再添加 16 个字节,这不会影响对齐。)所以我们的 516 必须四舍五入到下一个 16 的倍数,即 528。
如果编译器更加小心并且没有浪费 argc
和 argv
之间的 4 个字节,我们本可以只用 512 个字节。不过,使用 528 的一个好处是缓冲区 str
最终以 16 字节对齐。对于 char
的数组,其最小对齐仅为 1,这不是必需的,但它可以使 strcpy
等字符串函数更有效地使用快速 SIMD 算法。我不确定编译器是故意这样做还是只是巧合。
这是我的源代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 500
int main(int argc, char** argv)
{
if (argc != 2)
exit(1);
char str[MAX];
strcpy(str, argv[1]);
return 0;
}
我 disas
使用 gdb
组合 main
并得到以下结果:
Dump of assembler code for function main:
0x0000000000001145 <+0>: push %rbp
0x0000000000001146 <+1>: mov %rsp,%rbp
0x0000000000001149 <+4>: sub [=11=]x210,%rsp
.
.
.
End of assembler dump.
这里值得注意的是:
0x0000000000001149 <+4>: sub [=15=]x210,%rsp
我的问题是-
为什么有 [=16=]x210
(528 字节),而我要求的应该是 [=17=]x1f4
(500 字节)?
我猜你正在使用 gcc 并在没有优化的情况下进行编译,比如 this (godbolt)。
这里发生了几件事:
首先,在没有优化的情况下进行编译时,编译器会尝试确保每个局部变量在内存中都有一个地址,以便调试器可以轻松地检查或修改它。这包括函数参数,这些参数在 x86-64 上以其他方式通过寄存器传递。因此,编译器需要分配额外的堆栈 space,其中 argc
和 argv
参数可以“溢出”。您可以在程序集的第 5 行和第 6 行看到溢出:
movl %edi, -516(%rbp)
movq %rsi, -528(%rbp)
如果仔细观察,您可能会注意到编译器将 argc
(来自 %edi
)放置在地址 -516(%rbp)
处浪费了 4 个字节,而 -520(%rbp)
本来可用.原因还不完全清楚,但毕竟不是优化!这样我们就达到了 516 字节。
另一个问题是 x86-64 ABI 需要 16 字节堆栈对齐;参见 rbp
再添加 16 个字节,这不会影响对齐。)所以我们的 516 必须四舍五入到下一个 16 的倍数,即 528。
如果编译器更加小心并且没有浪费 argc
和 argv
之间的 4 个字节,我们本可以只用 512 个字节。不过,使用 528 的一个好处是缓冲区 str
最终以 16 字节对齐。对于 char
的数组,其最小对齐仅为 1,这不是必需的,但它可以使 strcpy
等字符串函数更有效地使用快速 SIMD 算法。我不确定编译器是故意这样做还是只是巧合。