【发布时间】:2018-09-17 08:33:39
【问题描述】:
我在 C 中有 2 个函数:
void func1(unsigned char x)
{
unsigned char a[10][5];
a[0][0] = 1;
a[9][4] = 2;
}
void func2(unsigned char x)
{
unsigned char a[10][5];
a[0][0] = 1;
a[9][4] = 2;
unsigned char b[10];
b[0] = 4;
b[9] = 5;
}
编译:
gcc 7.3 x86-64
-O0 -g
操作系统:
16.04.1-Ubuntu x86-64
生产的intel函数汇编:
func1(unsigned char):
pushq %rbp
movq %rsp, %rbp
movl %edi, %eax
movb %al, -68(%rbp)
movb $1, -64(%rbp)
movb $2, -15(%rbp)
nop
popq %rbp
ret
func2(unsigned char):
pushq %rbp
movq %rsp, %rbp
movl %edi, %eax
movb %al, -84(%rbp)
movb $1, -64(%rbp)
movb $2, -15(%rbp)
movb $4, -74(%rbp)
movb $5, -65(%rbp)
nop
popq %rbp
ret
我可以看到,对于 50 字节数组,分配了 64 个字节。似乎它在 16 字节边界上分配堆栈,因为对于 10 字节 - 分配了 16 字节。
我的问题:
1) 数组的 16 字节边界是否有一些堆栈对齐标准? 因为 int、long int 等变量显然没有分配在 16 字节边界上。
2) 为什么数组分配的方式如此奇怪?我们可以看到数组 a,14 字节 作为对齐填充添加了就在我们的 50 字节有效负载之后,但对于数组 @ 987654325@,6 个对齐字节被分配在我们的 10 个字节的有效负载之前。我错过了什么吗?
3) 为什么函数参数传入EDI (unsigned char x),放置在距离我们数组内存开头4字节的堆栈上(包括填充)。那么字节变量(AL register)也被填充了还是什么?为什么是 4 个字节?
【问题讨论】:
-
不同的 C 编译器可能会输出不同的代码。另一方面,检查未优化的构建充其量是徒劳的。除此之外,从实际的角度来看,您的问题是有价值的。
-
On 2) & 3): Option
-O0表示“尽快生成代码。不要浪费任何时间考虑改进代码的选项。” 而且代码看起来确实可以使用一些改进... -
C语言中没有栈
-
rsp本身的对齐保持不变,因此如果一个函数需要对任何东西进行如此多的对齐(例如加载/存储 SSE 向量),它可以免费获得。 Why does System V / AMD64 ABI mandate a 16 byte stack alignment?。这当然并不意味着堆栈上的每个对象都是 16 字节对齐的。 GCC 甚至在叶函数(不进行任何函数调用)中也会这样做,就像实现细节一样。
标签: c arrays assembly memory stack-memory