堆栈上的数组内存分配

too*_*zzy 1 c memory arrays assembly stack-memory

我在 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;
}
Run Code Online (Sandbox Code Playgroud)

编译:

gcc 7.3 x86-64

-O0 -g

操作系统:

16.04.1-Ubuntu x86-64

生产的英特尔功能组装:

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
Run Code Online (Sandbox Code Playgroud)

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

我的问题:

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

2)为什么数组以如此奇怪的方式分配?我们可以看到,对于数组a,在50 个字节的有效负载之后添加了14 个字节作为对齐填充,但对于数组,我们的10 个字节的有效负载之前分配了6 个对齐字节。我错过了什么吗?b

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

ens*_*nsc 5

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");
}
Run Code Online (Sandbox Code Playgroud)

原因:

gcc -O1 -g x.c -c
objdump -d x.o
0000000000000010 <func2>:
  10:   c6 44 24 c0 01          movb   $0x1,-0x40(%rsp)
  15:   c6 44 24 f1 02          movb   $0x2,-0xf(%rsp)
  1a:   c6 44 24 b6 04          movb   $0x4,-0x4a(%rsp)
  1f:   c6 44 24 bf 05          movb   $0x5,-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   
Run Code Online (Sandbox Code Playgroud)

-Os-O3创建其他布局。