在堆栈上的数组内存分配

时间:2018-04-07 12:58:44

标签: c arrays assembly memory stack-memory

我在C中有两个函数:

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 $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字节的有效负载之后添加为对齐填充,但是数组b 6对齐字节 10字节的有效负载之前分配。我错过了什么吗?

3)为什么函数参数在EDIunsigned char x)中传递,放置在离我们的数组内存开始的4个字节的堆栈(包括填充)。那么字节变量(AL register)也是填充的还是什么?为什么 4个字节

1 个答案:

答案 0 :(得分:4)

x86_64 abi需要16字节堆栈对齐(进入函数时堆栈指针必须对齐16字节)。但是你看到的那个人是由-O0引起的;使用-O1或更高版本,阵列可以更有效地对齐。 E.g。

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   $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   

-Os-O3创建其他布局。