有没有办法防止组合 class 的填充

Is there a way to prevent padding of a composed class

假设一台 64 位机器:

如果我从 class A:

开始
struct A
{
    int* a1; //8
    int* a2; //8 
    uint16_t a3; //2
    uint16_t a4; //2

    uint32_t a5; //4
    uint32_t a6; //4
    uint32_t a7; //4
};

现在所有成员都正确对齐了,A的大小是32字节,从a5a1的偏移量是20字节

现在,如果我尝试按如下方式重构它:

struct A_part1
{
    int* a1; //8
    int* a2; //8 
    uint16_t a3; //2
    uint16_t a4; //2
};

struct A_new
{
    A_part1 a1; 
    uint32_t a5; //4
    uint32_t a6; //4
    uint32_t a7; //4
};

现在 A_new 的大小是 40 字节,因为 A_part1 被填充到 24 字节并且 A_new 随后从 36 填充到 40 字节。

大概编译器试图确保连续的 A_part1 对齐。

如果我知道我只会在 A 中使用 A_part1,这是我必须在 a_part1 中使用 pragma 包的唯一选择确保 A_new 将具有与原始 A?

相同的特征

你是对的:标准 C++ 中没有办法指定 struct 的填充布局,因此你只能使用 compiler-specific 控制机制,如 #pragma pack

If I know that I will only ever use A_part1 in A, is the only option I have to use a pragma pack on a_part1 here to ensure that A_new will have the same characteristics as the original A?

没有。 pragma 不是指定布局和填充的唯一方法。

在我曾经工作过的一个嵌入式软件中,我们找到了几种解决 pragma 问题的方法。


C 和 C++ 均未提供内存布局语义。

但是一种易于理解的技术(至少在逐个字段的基础上)是明确编码字节驻留在 class 或结构中的位置。通过重构和添加小字节移动方法,工作量大大减少。

注意:七个字段很小,因此 'easy'。五十田很累。一百个字段是"lets write some code to write some code."你的容忍度会有所不同。

此技术明确地将字节 byte-by-byte 移动到 class 数据中。优点:1)没有填充(除非你想要它)。 2) 对编译器选项变化不敏感。 3) 便携。

小例子(编译,但未测试)(注意:任意字节序选择,我们使用常规endian-transformation)

class A
{
   //   int* a1;     //8  0..7
   //   int* a2;     //8  8..15
   //   uint16_t a3; //2  16..17
   //   uint16_t a4; //2  18..19
   //
   //   uint32_t a5; //4  etc
   //   uint32_t a6; //4
   //   uint32_t a7; //4
   // ...

   // modify field a3
   void a3(uint16_t val) {
      // if needed, correct val for destination endianess ------vvv
      data[Oa3+0] = static_cast<uint8_t>((val >> 0) & 0xff); // LSB
      data[Oa3+1] = static_cast<uint8_t>((val >> 8) & 0xff); // MSB
   }

   // access field a3
   uint16_t  a3() {
      // if needed, correct val for endianess ---------------------vvv
      uint16_t val = static_cast<uint16_t>((data[Oa3+0] << 0) + // LSB
                                           (data[Oa3+8] << 8)); // MSB
      return (val);
   }

   // continue for each field

private:
   enum {
      Oa1 = 0, // Offset a1
      Oa2 = 8,
      Oa3 = 16
      //... etc
   };

   uint8_t data[(8+8+2+2+(3*4))]; 
};