GCC如何在x64中对齐结构?

GCC如何在x64中对齐结构?,c,gcc,assembly,x86-64,memory-alignment,C,Gcc,Assembly,X86 64,Memory Alignment,TL;DR 当将堆栈对齐到16字节的倍数时,为什么struct的填充在较高的地址,而基元类型的填充在较低的地址 更多详细信息 首先,我的机器遵循SystemV AMD64 ABI,我知道堆栈对齐到16字节。但是当我试图理解结构如何存储在堆栈上时,我发现了一些我不理解的GCC的有趣行为 我有以下计划: 结构{ 龙我; }; 无效获取结构地址(结构S*sp); 无效取长地址(长*l); 无效访问\u成员(){ 结构; 获取结构地址(&s); } 无效访问\u本地(){ 长l; 取长地址(&l); }

TL;DR

当将堆栈对齐到16字节的倍数时,为什么struct的填充在较高的地址,而基元类型的填充在较低的地址

更多详细信息

首先,我的机器遵循SystemV AMD64 ABI,我知道堆栈对齐到16字节。但是当我试图理解结构如何存储在堆栈上时,我发现了一些我不理解的GCC的有趣行为

我有以下计划:

结构{
龙我;
};
无效获取结构地址(结构S*sp);
无效取长地址(长*l);
无效访问\u成员(){
结构;
获取结构地址(&s);
}
无效访问\u本地(){
长l;
取长地址(&l);
}
我使用以下命令编译它:

$gcc-ansi-pedantic-S-Og访问成员.c
我得到以下汇编代码:

    .file   "access-member.c"
    .text
    .globl  access_member
    .type   access_member, @function
access_member:
.LFB0:
    .cfi_startproc
    subq    $24, %rsp
    .cfi_def_cfa_offset 32
    movq    %rsp, %rdi # <---------- start address of the struct
    call    take_struct_addr
    addq    $24, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE0:
    .size   access_member, .-access_member
    .globl  access_local
    .type   access_local, @function
access_local:
.LFB1:
    .cfi_startproc
    subq    $24, %rsp
    .cfi_def_cfa_offset 32
    leaq    8(%rsp), %rdi # <---------- start address of the long
    call    take_long_addr
    addq    $24, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE1:
    .size   access_local, .-access_local
    .ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-39)"
    .section    .note.GNU-stack,"",@progbits
.file“access member.c”
.文本
.globl access_成员
.type access_member,@function
访问小组成员:
.LFB0:
.cfi_startproc
subq$24,%rsp
.cfi_def_cfa_偏移量32

movq%rsp%rdi#这没什么区别。只要满足对齐要求,编译器就可以自由地确定堆栈框架中局部变量的布局。它可能是编译器内部构件的产物。您可以声明另一个局部变量并将其打包到同一个框架中。查看堆栈变量布局的一种简便方法是声明它们
volatile
,然后初始化它们<代码>易失性长foo=8。顺便说一句,你的
struct{long}继承其最对齐的成员的alignof(),没有应用额外的规则,因此它只是一个8字节对齐的对象,可以去任何地方。@Jester是的,如果我添加更多变量,堆栈帧对齐仍然有效。我只是好奇填充物的位置。是否由编译器决定是在局部变量之前还是之后填充?还是有一个不同的编译器将遵循的规范?@PeterCordes,谢谢。但是,添加
volatile
不会影响填充位置。当满足对齐条件时,是否完全由编译器决定将填充放在何处?或者有不同的编译器将遵循的规范吗?我不是这么说的。我说过,您可以在启用优化的情况下编译,并查看包含
volatile struct S={1}的函数
可变长l=2
以查看编译器将它们放置在何处。不必禁用优化和/或调用
take\u struct\u addr
。当然,
alignof(S)=8仍然。