C结构内存布局?

我有C#背景。 非常新鲜的低级语言像C.

在C#中,默认情况下由编译器布置的结构内存。 编译器可以重新sorting数据字段或隐式地在字段之间填充附加位。 所以我不得不指定一些特殊的属性来覆盖这个行为的确切布局。

AFAIK,C默认情况下不重新sorting或alignment结构的内存布局。 但是我听说有一些很难find的例外。

什么是C的内存布局行为? (应该重新排列/排列而不是)

在C中,允许编译器为每个基本types指定一些alignment方式。 通常alignment是types的大小。 但它完全是特定于实现的。

引入填充字节,因此每个对象都被正确alignment。 重新sorting是不允许的。

可能每个远程现代编译器都实现了#pragma pack ,它允许对填充进行控制,并将其留给程序员以符合ABI。 (虽然这是严格的非标准的)。

从C99§6.7.2.1开始:

12结构体或联合体对象中的每个非位域成员都按照与其types相适应的实现定义的方式进行alignment。

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

它是特定于实现的,但实际上这个规则(在没有#pragma pack之类的情况下)是:

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

所以,给出以下结构:

 struct ST { char ch1; short s; char ch2; long long ll; int i; }; 
  • ch1在偏移量0
  • 填充字节插入alignment…
  • s在偏移2
  • ch2在s之后立即偏移4
  • 插入3个填充字节以alignment…
  • 在偏移8
  • 我是在16点之后,在ll之后
  • 在结尾添加4个填充字节,以便整个结构是8个字节的倍数。 我在64位系统上检查了这个:32位系统可能允许结构有4个字节alignment。

所以sizeof(ST)是24。

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

 struct ST { long long ll; // @ 0 int i; // @ 8 short s; // @ 12 char ch1; // @ 14 char ch2; // @ 15 } ST; 

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

从维基百科的文章 :

数据alignment意味着将数据置于等于字长的几倍的存储器偏移处,由于CPU处理内存的方式而增加了系统的性能。 要alignment数据,可能需要在最后一个数据结构的末尾和下一个数据结构填充的开始之间插入一些无意义的字节。

从GCC文档的6.54.8结构包装Pragma :

为了与Microsoft Windows编译器兼容,GCC支持一组#pragma指令,这些指令改变了随后定义的结构成员(零宽度位域除外),联合和类的最大alignment。 下面的n值总是要求是2的小幂,并以字节为单位指定新的比对。

  1. 编译包(n)只是设置新的alignment方式。

  2. pragma pack()将alignment设置为所在的alignment方式

    编译开始时起作用(另请参阅命令行选项-fpack-struct [=]请参阅代码Gen选项)。

  3. 编译包(push [,n])将当前的alignment设置推到

    内部堆栈,然后可以select设置新的alignment方式。

  4. 编译包(pop)将alignment设置恢复为保存的alignment设置

    内部堆栈的顶部(并删除该堆栈条目)。 请注意, enter code here #pragma pack([n])不会影响这个内部堆栈; 因此可以有#pragma pack(push)后跟多个#pragma pack(n)实例,并由一个#pragma pack(pop)来完成。

一些目标,例如i386和powerpc,支持ms_struct #pragma,它将结构作为logging的__attribute__((ms_struct))进行布局。

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

  2. 编译指示ms_structclosures声明结构的布局。

  3. 杂注ms_struct重置回到默认布局。

在C中,结构几乎与您在代码中指定的完全一致。 类似于C#的StructLayout.Sequential。

唯一的区别在于成员alignment。 这不会重新排列结构中的数据成员,但可以通过在结构中插入“填充”字节来更改结构的大小。 这是为了确保每个成员都在一个边界上(通常是4或8个字节)开始。

例如:

 struct mystruct { int a; short int b; char c; }; 

这个结构的大小通常是12个字节(每个成员4个)。 这是因为大多数编译器默认情况下会使每个成员的大小与结构中的最大值相同。 所以char将占用4个字节而不是一个字节。 但是请注意,sizeof(mystruct :: c)仍然是1,但是sizeof(mystruct)将是12。

可能很难预测编译器如何填充/alignment结构。 大多数会默认,因为我已经解释了上面,有些将默认没有填充/alignment(有时也被称为“打包”)。

改变这种行为的方法是非常依赖于编译器的,语言中没有任何内容指定如何处理。 在MSVC中,您将使用#pragma pack(1)closuresalignment方式(1表示alignment1字节边界上的所有内容)。 在GCC中,你可以在结构定义中使用__attribute__((packed)) 。 请参阅您的编译器的文档以查看默认情况下以及如何更改该行为。