了解堆栈分配和对齐

我试图理解堆栈对齐是如何工作的,如什么是“堆栈对齐”? 但我很难得到一个小例子来certificate上述行为。 我正在检查我的函数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 : push %ebp 0x08048395 : mov %esp,%ebp 0x08048397 : sub $0x20,%esp 0x0804839a : movl $0x0,-0x4(%ebp) 0x080483a1 : movb $0x61,-0x14(%ebp) 0x080483a5 : leave 0x080483a6 : ret End of assembler dump. 

我的堆栈以16个字节的块分配(我通过其他几个测试validation了这一点)。 根据汇编程序转储,这里分配了32个字节,因为(16 <4 + 16 <32),但我希望在前16个字节上分配整数'a',然后在接下来的16个字节上分配字符数组(在中间留下12个字节的空间)。 但似乎整数和字符数组都被分配了一个20字节的连续块,根据我上面提到的讨论,这是低效的。 有人可以解释我在这里缺少的东西吗?

编辑 :我得出的结论是我的堆栈以16字节的块分配,程序如下:

 void foo() { char a[1]; } 

和相应的汇编程序转储:

 (gdb) disassemble foo Dump of assembler code for function foo: 0x08048394 : push %ebp 0x08048395 : mov %esp,%ebp 0x08048397 : sub $0x10,%esp 0x0804839a : leave 0x0804839b : ret End of assembler dump. 

您可以看到,堆栈上已为大小为1的字符数组分配了16个字节(仅需1个字节)。 我可以将数组的大小增加到16并且汇编程序转储保持不变,但是当它为17时,它会在堆栈上分配32个字节。 我运行了很多这样的样本,结果是一样的; 堆栈内存以16个字节的块分配。 在堆栈分配,填充和对齐中已经讨论了类似的主题,但我更热衷于找出的是为什么对齐在我的示例中没有影响。

我认为你错过了这样一个事实,即不需要将所有堆栈变量单独对齐到16字节边界。

通常的规则是变量在32位边界上分配。 我不确定为什么你认为16字节有任何特殊含义。

我从来没有听说过具体的堆栈对齐这样的事情。 如果CPU存在对齐要求,则无论是存储在堆栈还是其他地方,都要对所有类型的数据存储器进行对齐。 它从偶数地址开始,后面有16位,32位或64位数据。

16个字节可能是某种片上高速缓冲存储器优化,虽然这对我来说似乎有点牵强。

一个很好的例子就是在结构上看到这个。

 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

希望它有所帮助。

您可以使用名为pahole http://packages.debian.org/lenny/dwarves的工具检查为数据结构分配额外内存的方式。 它向您显示程序的所有漏洞:如果总结数据的大小,以及卡住时分配的实际大小