C中的结构内存布局


85

我有C#背景。我是C之类的底层语言的新手。

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

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

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

Answers:


110

在C语言中,允许编译器为每种原始类型指定某种对齐方式。通常,对齐方式是类型的大小。但这完全是特定于实现的。

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

可能每个远程的现代编译器实现#pragma pack都可以控制填充,并将填充留给程序员以符合ABI。(不过,这绝对是非标准的。)

根据C99§6.7.2.1:

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

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


1
一些编译器(例如,GCC)实现了#pragma pack与之相同的效果,但对语义的控制更为精细。
克里斯·卢茨

21
看到票数不足,我感到很惊讶。谁能指出错误?
Potatoswatter 2010年

2
C11也有_Alignas
idmean

117

它是特定于实现的,但是在实践中,规则(不存在#pragma pack或类似条件)是:

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

因此,给出以下结构:

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
  • 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;

3
如有必要,在...之前添加填充。最好char在您的示例中添加最终成员。
重复数据删除器2014年

9
基本类型不一定需要sizeof(T)字节对齐。例如,double常见的32位体系结构为8字节,但通常仅需要4字节对齐。此外,仅在结构端部的填充会垫到最宽的结构成员的对齐方式。例如,一个3个char变量的结构可以没有填充。
马特

1
@ dan04,按sizeof(T)降序排列结构是一种好习惯吗?这样做会有不利之处吗?
RohitMat

11

您可以先阅读数据结构对齐维基百科文章,以更好地理解数据对齐。

维基百科文章

数据对齐意味着将数据放在等于字长的倍数的内存偏移处,这由于CPU处理内存的方式而提高了系统的性能。为了对齐数据,可能有必要在最后一个数据结构的末尾与下一个数据结构的末尾(即数据结构填充)之间插入一些无意义的字节。

从GCC文档的6.54.8结构打包语法开始

为了与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(push) 跟随多个#pragma pack(n) 实例并由一个实例终结 #pragma pack(pop)

一些目标(例如i386和powerpc)支持ms_struct #pragma,该ms_struct列出了所记录的结构 __attribute__ ((ms_struct))

  1. #pragma ms_struct on 打开声明的结构的布局。
  2. #pragma ms_struct off 关闭声明的结构的布局。
  3. #pragma ms_struct reset 返回到默认布局。

感谢您的照顾。我按照您的指示修改了问题。
2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.