如果我对结构的字段进行布局以便它们不需要任何填充,那么符合标准的 C++ 编译器是否可以添加额外的内容?

If I lay out the fields of my struct so they shouldn't need any padding, can a conforming C++ compiler add extra anyway?

我想用 C++ 以现有的二进制协议格式(我正在编写一个 memcached 客户端)格式化数据包。在 C 中,我可以这样做:

    typedef struct {
        uint8_t magic;
        uint8_t opcode;
        uint16_t keylen;
        uint8_t extlen;
        uint8_t datatype;
        uint16_t reserved;
        uint32_t bodylen;
        uint32_t opaque;
        uint64_t cas;
    } request_header;

在 C++ 中,编译器通常可以在字段之间添加填充。然而,上面的结构是经过仔细布局的,因此所有内容都可以在没有填充的情况下对齐,假设 n 位类型只需要在 n 位边界上对齐。那么在 C++ 中,按照标准,我安全吗?或者符合标准的 C++ 编译器是否可以添加额外的填充,从而阻碍我使用它来布置我的位?

你是对的,C++ 可以任意填充。来自 C++.11 §9.2¶14(重点是我的):

Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).

C 也允许添加填充字节,所以这不是 C++ 特有的。来自 C.11 §6.7.2.1¶15(重点是我的):

Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

如果你想避免填充,唯一最大限度可移植的方法是在发送时将数据结构自己打包到连续内存中(例如,vector),并在发送时将序列化数据解包到你的数据结构中接收。您的编译器可能会提供扩展,以允许您在 struct 内保持所有成员连续(例如,GCC 的 packed 属性,或 VC++ 的 pack pragma,如前所述here).

在 C++ 中有这样一个东西,叫做 POD for plain-old-data。基本上,如果满足某些限制,C++ 中的结构是 POD,它将与 C 代码中定义的相同结构逐字节兼容。

要成为 POD,结构必须没有访问说明符(public,私有),并且没有非静态成员函数,包括运算符、构造函数和析构函数。

这不值得担心,让编译器告诉你这很奇怪:

  static_assert(sizeof(request_header) == 24, "Unexpected packet size");