有啥方法可以控制 C++ 中结构成员(包括位字段)之间的填充?
Posted
技术标签:
【中文标题】有啥方法可以控制 C++ 中结构成员(包括位字段)之间的填充?【英文标题】:Is there any way to control the padding between struct members (incl. bit field) in C++?有什么方法可以控制 C++ 中结构成员(包括位字段)之间的填充? 【发布时间】:2013-09-10 06:41:09 【问题描述】:我正在解析网络数据流,我想知道是否有任何方法可以将数据流直接映射到数据结构。
例如,我想定义一个RTP协议的数据结构如下。
class RTPHeader
int version:2; // The first two bits is version.
int P:1; // The next bits is an field P.
int X:1;
int CC:4;
int M:1;
int PT:7;
int sequenceNumber;
int64 timestamp;
.....
;
并以这种方式使用它。
RTPHeader header;
memcpy(&header, steamData, sizeof(header));
但是由于 C++ 编译器会在成员之间插入填充,有没有什么方法可以控制成员之间不添加填充(包括位字段成员)?
这个问题与How to get rid of padding bytes between data members of a struct 不重复,因为在我的示例中可能存在位域。
【问题讨论】:
我相信没有标准的方法可以做到这一点,但你的编译器可能有一些扩展(例如 VS 有它)允许你控制填充。我希望有人证明我错了。 位域加起来是 8 的倍数,(不出所料)。如果你指示编译器打包类/结构,你应该没问题。 我不认为位域之间添加了填充,是吗?否则它会破坏位域的意义。 @JonathanPotter 如果位域加起来不是 8 的倍数,编译器不会跨字节边界分割后续整数等,因此它会填充。 @MartinJames:对,但不是在位域之间。 【参考方案1】:如果您能够使用 C++11,则可以利用使用 alignof
运算符实现的对齐控件。
如果您不能使用 C++11 编译器,可以使用非标准替代方案来帮助您;在 GCC 中为 __attribute__(packed)
,在 MSVC 中为 #pragma pack
。
如果您选择的是 GCC 变体,则该属性必须放在 结构的末尾:
class RTPHeader
int version:2; // The first two bits is version.
int P:1; // The next bits is an field P.
int X:1;
int CC:4;
int M:1;
int PT:7;
int sequenceNumber;
int64 timestamp;
.....
__attribute__((packed)) ; // attribute here!
如果您选择的是 MSVC,则编译指示必须放在 结构之前:
#pragma pack(1) // pragma here!
class RTPHeader
int version:2; // The first two bits is version.
int P:1; // The next bits is an field P.
int X:1;
int CC:4;
int M:1;
int PT:7;
int sequenceNumber;
int64 timestamp;
.....
;
如果您的代码必须同时编译,唯一的方法(没有 C++11 alignof
运算符)是条件编译:
#ifdef MSVC
#pragma pack(1)
#endif
class RTPHeader
int version:2; // The first two bits is version.
int P:1; // The next bits is an field P.
int X:1;
int CC:4;
int M:1;
int PT:7;
int sequenceNumber;
int64 timestamp;
.....
#ifdef GCC
__attribute__((packed));
#else
;
#endif
【讨论】:
【参考方案2】:只要您不要求此代码在任意机器上“工作” - 例如对 int
所在的字节边界有限制的机器(通常是 4 字节边界),然后使用
#pragma(pack)
应该可以工作,它是supported in GCC 以及 Microsoft 和“Microsoft 插件兼容”编译器(例如 Intel 的编译器)。
但请注意,并非所有处理器都支持非对齐访问,因此以 16 位值开始一个块,然后是 32 位 int
可能会导致问题。
我还会为序列号使用一个大小合适的整数,以确保它在每个编译器中都是 32 位,而不是突然变成 16 位或 64 位。
另请注意,C++ 标准没有说明位存储在位域中的顺序 - 或者就此而言,它们之间是否存在间隙。尽管您可以期望按照字节顺序存储位域(小端机器首先从最低位开始,大端机器首先从最高位开始),但该标准在这方面没有说明任何内容。
【讨论】:
我会将这篇文章标记为答案,因为它不仅回答了问题,还指出了 C++ 中未定义位字段存储以及小端问题。实际上在考虑了字节序问题后,我不想选择这种解决方案来解析流。【参考方案3】:为了避免插入填充字节,您可以使用
#pragma pack(push,n) // use n = 1 to have 1 Byte resolution
typedef struct ...MY_STRUCT;
#pragma pack(pop)
这对我来说很好。
还要考虑结构成员对齐的编译选项
/Zp1
但请记住,这将对您的整个项目产生影响。
【讨论】:
然而,这是一个依赖于平台的编译器扩展(即使 gcc 和 VS 使用相同的语法,也不知道)。而且命令行选项绝对是VS only。以上是关于有啥方法可以控制 C++ 中结构成员(包括位字段)之间的填充?的主要内容,如果未能解决你的问题,请参考以下文章