在C结构存储器布局在C结构存储器布局(Struct memory layout in C)

2019-05-10 09:48发布

我有一个C#的背景。 我非常新手到像C.低层次的语言

在C#, struct的存储器是由编译器通过默认布局。 编译器可以重新排序的数据字段或隐式场之间垫的附加位。 所以,我必须指定一些特殊的属性来覆盖这个行为进行精确布局。

据我所知,C不会重新排序或对齐的内存布局struct默认。 不过,我听说有一个小的例外,这是非常难找到。

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

Answer 1:

在C中,编译器允许决定为每个原语类型一些对准。 典型地,对准是该类型的大小。 但它完全实现特定的。

填充字节引入,每个对象定位正确。 重新排序是不允许的。

也许每一个远程现代编译器实现#pragma pack ,允许控制填充和它留给程序员遵守ABI。 (这是严格标准的,虽然)。

从C99§6.7.2.1:

12结构或联合对象的每个非位字段构件在适合其类型的实现定义的方式被排列。

13内的结构对象,非位字段构件和单元,其中位字段驻留具有在其声明的顺序增加的地址。 一个指向结构对象,适当地转换,点到它的初始成员(或如果该构件是一个位字段,然后到单元在其中驻留),并且反之亦然。 有可能是一个结构对象中的无名进行填充,而不是在其开始。



Answer 2:

这是实现特定的,但在实践中的规则(在没有#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是在偏移16,LL之后
  • 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;


Answer 3:

您可以通过读取启动数据结构对齐维基百科的文章 ,以更好地了解数据对齐的。

从维基百科的文章 :

数据对准装置把在存储器偏移量等于字尺寸,这提高了系统的性能的某个倍数的数据由于CPU处理存储器的方式。 要对齐的数据,可能有必要在最后一个数据结构的末尾和下一个的开始,这是数据结构填充之间插入一些无意义字节。

从6.54.8结构-包装编译指示海合会文档:

对于与Microsoft Windows兼容的编译器,GCC支持随后定义了一组#pragma指令改变其结构(比零宽度位字段以外),工会成员的最大对齐,和类。 下面总是n值需要是2的较小功率,并指定以字节为单位的新的取向。

  1. 编译组(N),简单地设置新的定位。

  2. 编译包()设置对准到所述一个那是在

    效果当编译开始(还参见命令行选项-fpack-结构[=]见代码根选项)。
  3. 编译包(推[,n])的推压的当前对齐设置

    内部堆栈,然后任选设置新对准。
  4. 编译包(POP)恢复对齐设置到一个保存在

    内部堆栈的顶部(并移除堆栈条目)。 请注意, enter code here的#pragma包([N])不会影响这个内部栈; 因此有可能具有随后的#pragma多个包(n)的实例,并通过单一的#pragma包(POP)定稿的#pragma包(推)。

一些目标,例如I386和PowerPC,支持ms_struct的#pragma其中规定了一个结构为记录__attribute__((ms_struct))。

  1. 编译ms_struct上的布局结构的声明转弯。

  2. 编译ms_struct关闭将关闭声明结构布局。

  3. 编译ms_struct复位返回到默认布局。



文章来源: Struct memory layout in C