96

我有 C# 背景。我是 C 等低级语言的新手。

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

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

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

4

3 回答 3

130

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

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

因此,给定以下结构:

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
  • ch1位于偏移量 0
  • 插入填充字节以对齐...
  • s在偏移 2
  • ch2在偏移量 4 处,紧接在 s 之后
  • 插入 3 个填充字节以对齐...
  • ll在偏移 8
  • i在偏移量 16 处,就在 ll 之后
  • 最后添加了 4 个填充字节,因此整个结构是 8 个字节的倍数。我在 64 位系统上检查了这一点:32 位系统可能允许结构具有 4 字节对齐。

24也是如此sizeof(ST)

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

struct ST
{
   long long ll; // @ 0
   int i;        // @ 8
   short s;      // @ 12
   char ch1;     // @ 14
   char ch2;     // @ 15
} ST;
于 2010-05-01T06:20:49.497 回答
115

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

引入了填充字节,因此每个对象都正确对齐。不允许重新排序。

可能每个远程现代编译器都实现#pragma pack了允许控制填充并将其留给程序员以遵守 ABI。(不过,这完全是非标准的。)

从 C99 §6.7.2.1 开始:

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

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

于 2010-05-01T05:26:25.140 回答
12

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

来自维基百科文章

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

从GCC 文档的6.54.8 Structure-Packing Pragmas开始:

为了与 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,它将结构布局为文档中的 __attribute__ ((ms_struct)).

  1. #pragma ms_struct on打开声明的结构的布局。
  2. #pragma ms_struct off关闭声明结构的布局。
  3. #pragma ms_struct reset回到默认布局。
于 2010-05-01T05:26:01.100 回答