【问题标题】:C struct elements alignment (ansi)C 结构元素对齐 (ansi)
【发布时间】:2013-12-20 15:17:48
【问题描述】:

只是一个简单的问题...标准对结构成员对齐有何规定? 例如这个:

struct
{
    uint8_t a;
    uint8_t b;
    /* other members */
} test;

保证b在struct start的偏移量1处? 谢谢

【问题讨论】:

  • 我很晚才将这个添加到我的答案中,不知道我是怎么忘记的,但offsetof 可能会有所帮助。

标签: c++ c standards ansi memory-alignment


【解决方案1】:

标准(从 C99 开始)实际上并没有说明什么。

唯一真正的保证是(void *)&test == (void *)&a,并且a 的地址低于b。其他一切都取决于实施。

【讨论】:

  • 很多编译器都有相当方便的库宏offsetof
  • C11 有 _Alignof,ANSI C 没有。
  • 第一个无混淆保证:a 位于偏移量0
【解决方案2】:

C11 6.7.2.1 结构和联合说明符 p14 说

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

意味着您不能对ab 的地址之间的差异做出任何可移植的假设。

【讨论】:

    【解决方案3】:

    应该可以使用offsetof来确定成员的偏移量。

    对于 C,对齐是实现定义的,我们可以在草案 C99 标准部分 6.7.2.1结构和联合说明符12 段中看到(在 C11 中它将是第 14 段)说:

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

    13段说:

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

    对于C++,我们在标准部分草案9.2Class members13段中有以下类似的引用:

    分配具有相同访问控制(第 11 条)的(非联合)类的非静态数据成员,以便后面的成员在类对象中具有更高的地址。未指定具有不同访问控制的非静态数据成员的分配顺序(第 11 条)。实现对齐要求可能会导致两个相邻的成员不能立即分配;

    19段说:

    一个指向标准布局结构对象的指针,使用 reinterpret_cast 适当转换,指向它的 初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。 [ 笔记: 因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头, 必要时达到适当的对齐。 ——尾注]

    【讨论】:

      【解决方案4】:

      您使用的情况并不是真正的边缘情况,两个 uint_8 都足够小以适合内存中的同一个单词,将每个 uint_8 放入 uint_16 是没有用的。

      更严重的情况是这样的:

      {
          uint8_t a;
          uint8_t b;
      
          uint_32 c; // where is C, at &a+2 or &a+4 ?
          /* other members */
      } test;
      

      无论如何,这将始终取决于目标架构和您的编译器...

      【讨论】:

        【解决方案5】:

        K&R 第二版 (ANSI C) 在第 6.4 章(第 138 页)中说:

        但是,不要假设结构的大小是其成员大小的总和。由于不同对象的对齐要求,结构中可能存在未命名的“孔”。

        所以不,ANSI C 不保证b 位于偏移量 1。

        编译器甚至可能会将b 放在偏移量sizeof(int) 上,以便它与机器字的大小对齐,这样更容易处理。

        一些编译器支持pack-pragmas,因此您可以强制在struct 中没有这样的“漏洞”,但这不是可移植的。

        【讨论】:

          【解决方案6】:

          其他答案已经提到了 C 标准所保证的内容。

          但是,为了确保 b is 在偏移量 1 您的编译器可能会提供“打包”结构的选项,会说显式添加 no 填充.

          对于 gcc,这可以通过#pragma pack() 来实现。

          #pragma pack(1)
          struct
          {
            uint8_t a; /* Guaranteed to be at offset 0. */
            uint8_t b; /* Guaranteed to be at offset 1. */
            /* other members are guaranteed to start at offset 2. */
          } test_packed;
          #pragma pack()
          
          struct
          {
            uint8_t a; /* Guaranteed to by at offset 0. */
            uint8_t b; /* NOT guaranteed to be at offset 1. */
            /* other members are NOT guaranteed to start at offset 2. */
          } test_unpacked;
          

          一个可移植(和保存)的解决方案是简单地使用一个数组:

          struct
          {
            uint8_t ab[2]; /* ab[0] is guaranteed to be at offset 0. */
                           /* ab[1] is guaranteed to be at offset 1. */
            /* other members are NOT guaranteed to start at offset 2. */
          } test_packed;
          

          【讨论】:

          • 是的,但#pragma pack 不一定安全;见this question
          • @KeithThompson:这是正确的。所以我建议采用基于数组的解决方案。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-07-24
          • 2014-02-19
          • 2016-02-23
          • 1970-01-01
          • 1970-01-01
          • 2013-06-03
          相关资源
          最近更新 更多