堆栈上的数组内存分配

Arrays memory allocation on stack

我在 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

OS:

16.04.1-Ubuntu x86-64

生成的英特尔函数集:

func1(unsigned char):
  pushq %rbp
  movq %rsp, %rbp
  movl %edi, %eax
  movb %al, -68(%rbp)
  movb , -64(%rbp)
  movb , -15(%rbp)
  nop
  popq %rbp
  ret

func2(unsigned char):
  pushq %rbp
  movq %rsp, %rbp
  movl %edi, %eax
  movb %al, -84(%rbp)
  movb , -64(%rbp)
  movb , -15(%rbp)
  movb , -74(%rbp)
  movb , -65(%rbp)
  nop
  popq %rbp
  ret

我可以看到,对于 50 个字节的数组,分配了 64 个字节。它似乎在 16 字节边界上分配堆栈,因为对于 10 字节 - 分配了 16 字节。

我的问题:

1) 数组的 16 字节边界是否有一些堆栈对齐标准? 导致像 int、long int 等变量显然没有分配在 16 字节边界上。

2) 为什么数组分配方式如此奇怪?我们可以看到数组 a14 字节 被添加为对齐填充 紧随 我们的有效负载 50 bytes,但对于数组 b6 个对齐字节 被分配 before 我们 的有效负载10 个字节。我错过了什么吗?

3) 为什么函数参数在 EDI (unsigned char x) 中传递,放置在距数组内存开始(包括填充)4 个字节的堆栈中。所以字节变量(AL register)也被填充了还是什么?为什么 4 字节 ?

x86_64 abi 要求 16 字节堆栈对齐(进入函数时堆栈指针必须 16 字节对齐)。但是你看到的过度对齐是-O0造成的;使用 -O1 或更高,数组对齐更有效。例如

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;

    __asm__ __volatile__("" :: "r"(a), "r"(b) : "memory");
}

原因:

gcc -O1 -g x.c -c
objdump -d x.o
0000000000000010 <func2>:
  10:   c6 44 24 c0 01          movb   [=11=]x1,-0x40(%rsp)
  15:   c6 44 24 f1 02          movb   [=11=]x2,-0xf(%rsp)
  1a:   c6 44 24 b6 04          movb   [=11=]x4,-0x4a(%rsp)
  1f:   c6 44 24 bf 05          movb   [=11=]x5,-0x41(%rsp)
  24:   48 8d 44 24 c0          lea    -0x40(%rsp),%rax
  29:   48 8d 54 24 b6          lea    -0x4a(%rsp),%rdx
  2e:   c3                      retq   

-Os-O3 创建其他布局。