C 中的结构体内存布局

问题描述 投票:0回答:3

我有 C# 背景。我对 C 这样的低级语言来说是个新手。

在C#中,

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

AFAIK,默认情况下,C 不会重新排序或对齐

struct
的内存布局。不过,我听说有一个很难找到的例外。

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

c struct memory-layout
3个回答
150
投票

它是特定于实现的,但实际上规则(在没有

#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 字节对齐。

所以

sizeof(ST)
是 24。

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

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

121
投票

在 C 中,编译器可以为每个基本类型指定某种对齐方式。通常,对齐方式是类型的大小。但这完全是特定于实现的。

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

可能每个远程现代编译器都实现

#pragma pack
,它允许控制填充并将其留给程序员以遵守 ABI。 (不过,这完全是非标准的。)

来自 C99 §6.7.2.1:

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

13 在一个 结构体对象,非位域 会员及所在单位 位字段驻留的地址 按它们的顺序增加 被宣布。指向结构体的指针 对象,经过适当转换,指向 它的初始成员(或者如果该成员 是一个位域,那么到单位 它所在的位置),反之亦然。 内可能有未命名的填充 结构对象,但不是它的 开始。


14
投票

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

摘自维基百科文章

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

来自 GCC 文档的 6.54.8 Structure-Packing Pragmas

为了与微软兼容 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
    返回默认布局。
© www.soinside.com 2019 - 2024. All rights reserved.