我正在尝试理解堆栈对齐是如何工作的,如what is "stack alignment"?中所述,但我无法通过一个小例子来演示所述行为。我正在检查我的函数foo的堆栈分配:
void foo() {
int a = 0;
char b[16];
b[0] = 'a';
}
我用gcc -ggdb example.c -o example.out
编译了源文件(即没有任何编译器标志),gdb的汇编器转储读取:
(gdb) disassemble foo
Dump of assembler code for function foo:
0x08048394 <+0>: push %ebp
0x08048395 <+1>: mov %esp,%ebp
0x08048397 <+3>: sub $0x20,%esp
0x0804839a <+6>: movl $0x0,-0x4(%ebp)
0x080483a1 <+13>: movb $0x61,-0x14(%ebp)
0x080483a5 <+17>: leave
0x080483a6 <+18>: ret
End of assembler dump.
我的堆栈以16个字节的块分配(我在其他几个测试中验证了这一点)。根据汇编程序转储,这里分配了32个字节,因为(16 <4 + 16&lt; 32),但我希望在前16个字节上分配整数'a',然后在下一个字节上分配字符数组16个字节(中间留有12个字节的空格)。但似乎整数和字符数组都被分配了一个20字节的连续块,根据我上面提到的讨论,这是低效的。有人可以解释我在这里缺少的东西吗?
编辑:我得出结论,我的堆栈是以16字节的块分配的,程序如下:
void foo() {
char a[1];
}
相应的汇编程序转储:
(gdb) disassemble foo
Dump of assembler code for function foo:
0x08048394 <+0>: push %ebp
0x08048395 <+1>: mov %esp,%ebp
0x08048397 <+3>: sub $0x10,%esp
0x0804839a <+6>: leave
0x0804839b <+7>: ret
End of assembler dump.
您可以看到,对于大小为1的字符数组(仅需要1个字节),已在堆栈上分配了16个字节。我可以将数组的大小增加到16并且汇编程序转储保持不变,但是当它为17时,它在堆栈上分配32个字节。我运行了很多这样的样本,结果是一样的;堆栈内存以16字节的块分配。在Stack allocation, padding, and alignment中已经讨论了类似的主题,但我更热衷于找出为什么对齐对我的例子没有影响。
答案 0 :(得分:6)
我认为您错过了这样一个事实,即不需要将所有堆栈变量单独对齐到16字节边界。
答案 1 :(得分:0)
通常的规则是变量在32位边界上分配。我不确定为什么你认为16字节有任何特殊含义。
答案 2 :(得分:0)
我从未听说过特定堆栈对齐这样的事情。如果CPU存在对齐要求,则无论是存储在堆栈还是其他位置,都会对所有类型的数据存储器进行对齐。它从偶数地址开始,后面有16位,32位或64位数据。
16字节可能是某种片上高速缓冲存储器优化,虽然这对我来说似乎有点牵强。
答案 3 :(得分:0)
一个很好的例子是在结构上看到这一点。
struct a{
int a;
char b;
int c;
} a;
在32位系统上,如果单独使用,则为4 + 1 + 4字节。
因为结构和它的成员是对齐的“char b”将是4个字节,将其取为12个字节。
struct b{
int a;
char b;
int c;
} __attribute__((__packed__)) b;
使用packed属性可以强制它保持最小尺寸。因此,这个结构是9个字节。
您也可以查看http://sig9.com/articles/gcc-packed-structures
希望有所帮助。
答案 4 :(得分:0)
您可以使用名为pahole http://packages.debian.org/lenny/dwarves的工具检查为数据结构分配的额外内存。它向您显示程序的所有漏洞:如果总结数据的大小,以及卡住的实际大小