C中的结构内存布局

Eon*_*nil 76 c struct memory-layout

我有一个C#背景.我非常喜欢像C这样的低级语言.

在C#中,struct默认情况下由编译器布局内存.编译器可以隐式地重新排序数据字段或填充字段之间的附加位.因此,我必须指定一些特殊属性来覆盖此行为以获得精确布局.

AFAIK,C struct默认情况下不重新排序或对齐a的内存布局.但是,我听说有一个很难找到的例外.

什么是C的内存布局行为?什么应该重新订购/对齐而不是?

dan*_*n04 105

它是特定于实现的,但在实践中规则(在没有#pragma pack或类似的情况下)是:

  • Struct成员按其声明的顺序存储.(这是C99标准所要求的,如前所述.)
  • 如有必要,在每个结构成员之前添加填充,以确保正确对齐.
  • 每个基元类型T都需要对齐sizeof(T)字节.

所以,给定以下结构:

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
Run Code Online (Sandbox Code Playgroud)
  • ch1 在偏移0处
  • 插入填充字节以对齐...
  • s 在抵消2
  • ch2 在s之后立即偏移4
  • 插入3个填充字节以对齐...
  • ll 在抵消8
  • i 在ll之后,在偏移16处
  • 最后添加4个填充字节,以便整个结构是8个字节的倍数.我在64位系统上检查了这一点:32位系统可能允许结构具有4字节对齐.

sizeof(ST)是24.

通过重新排列成员可以将其减少到16个字节以避免填充:

struct ST
{
   long long ll; // @ 0
   int i;        // @ 8
   short s;      // @ 12
   char ch1;     // @ 14
   char ch2;     // @ 15
} ST;
Run Code Online (Sandbox Code Playgroud)

  • 原始类型不一定需要`sizeof(T)`字节的对齐.例如,常见的32位架构上的"double"是8个字节,但[通常只需要4个字节的对齐](http://stackoverflow.com/a/11110283/706054).此外,结构末端的填充仅填充最宽结构构件的对齐.例如,3个char变量的结构可能没有填充. (9认同)
  • 如果需要,之前添加填充...更像是之后.最好在你的例子中添加一个最终的`char`成员. (3认同)
  • @dan04,按 sizeof(T) 的降序排列结构是一个好习惯吗?这样做会有什么缺点吗? (2认同)

Pot*_*ter 102

在C中,允许编译器为每种基本类型指定一些对齐.通常,对齐是类型的大小.但它完全是针对具体实施的.

引入填充字节,以便每个对象都正确对齐.不允许重新排序.

可能每个远程现代编译器都实现#pragma pack了允许控制填充并将其留给程序员以符合ABI.(但它严格来说是非标准的.)

从C99§6.7.2.1:

12结构或联合对象的每个非位字段成员都以适合其类型的实现定义方式对齐.

13在结构对象中,非位字段成员和位字段所在的单元具有按声明顺序增加的地址.指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然.结构对象中可能存在未命名的填充,但不是在其开头.

  • 我很惊讶地看到了一个downvote.有谁可以指出错误? (17认同)
  • C11也有`_Alignas`. (2认同)

jsc*_*ier 10

您可以从阅读数据结构对齐维基百科文章开始,以更好地理解数据对齐.

来自维基百科的文章:

数据对齐意味着将数据放入存储器偏移量等于字大小的某个倍数,这会因CPU处理内存的方式而提高系统性能.为了对齐数据,可能需要在最后一个数据结构的末尾和下一个数据结构的开始之间插入一些无意义的字节,即数据结构填充.

6.54.8 GCC文档的结构包装编译:

为了与Microsoft Windows编译器兼容,GCC支持一组#pragma指令,这些指令可以更改随后定义的结构(除了零宽度位域),联合和类的成员的最大对齐.下面的n值始终需要是2的小幂,并以字节为单位指定新的对齐方式.

  1. pragma pack(n)只是设置新的对齐方式.

  2. pragma pack()将对齐设置为其中的对齐方式

    编译开始时的效果(另请参阅命令行选项-fpack-struct [=]请参阅代码选项选项).
  3. pragma pack(push [,n])按下当前对齐设置

    内部堆栈然后可选地设置新的对齐.
  4. pragma pack(pop)将对齐设置恢复为保存的对齐设置

    内部堆栈的顶部(并删除该堆栈条目).请注意, #pragma pack(n)#pragma pack([n])不会影响此内部堆栈; 因此可以使用#pragma pack(push),然后是多个#pragma pack(n)实例,并由单个#pragma pack(pop)完成.

某些目标,例如i386和powerpc,支持ms_struct #pragma,它将结构布局为记录的__attribute __((ms_struct)).

  1. pragma ms_struct on打开声明的结构的布局.

  2. pragma ms_struct off关闭声明的结构的布局.

  3. pragma ms_struct reset返回默认布局.