C++ 有没有办法控制C+中结构成员(包括位字段)之间的填充+;?

C++ 有没有办法控制C+中结构成员(包括位字段)之间的填充+;?,c++,c,padding,C++,C,Padding,我正在分析网络数据流,我想知道是否有任何方法可以将数据流直接映射到数据结构 例如,我想如下定义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; i

我正在分析网络数据流,我想知道是否有任何方法可以将数据流直接映射到数据结构

例如,我想如下定义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));
<> P>但C++编译器会在成员之间插入填充,有什么办法来控制,这样在成员(包括位字段成员)中添加了填充?


这个问题不是重复的,因为在我的示例中可以有位字段。

为了避免插入填充字节,可以使用

#pragma pack(push,n)   // use n = 1 to have 1 Byte resolution

typedef struct {...}MY_STRUCT;

#pragma pack(pop)
这对我很管用

也考虑结构成员对齐< /P>的编译选项

/Zp1

但请记住,这将对您的整个项目产生影响。

只要您不要求此代码在任意机器上“工作”——例如,对于int位于哪个字节边界(通常为4字节边界)有限制的机器,则使用

 #pragma(pack)
应该可以工作,而且它与Microsoft和“Microsoft插件兼容”编译器(如Intel的编译器)一样有效

但请注意,并非所有处理器都支持未对齐访问,因此以16位值开头的块,后跟32位
int
可能会导致问题

我还将为sequencenumber使用一个大小为的整数,以确保它在每个编译器中为32位,而不是突然为16或64位


注意,C++标准没有说明比特在比特字段中存储的顺序,也就是说,如果它们之间存在间隙,则不存在。虽然您可以期望根据字节顺序存储位字段(小端机器首先从最低位开始,大端机器首先从最高位开始),但该标准没有说明这方面的任何内容

如果您能够使用C++11,则可以利用操作符实现的align控件

如果你不能使用C++11编译器,有非标准的替代方案可以帮助你;在GCC和MSVC中

如果您选择的是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!
#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;
    .....
};
如果您选择的是MSVC,则pragma必须放在结构之前

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!
#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

我相信没有标准的方法可以做到这一点,但您的编译器可能有一些扩展(例如VS有它),允许您控制填充。我希望有人能证明我错了。比特域加起来是8的倍数,(这并不奇怪)。如果您指示编译器打包类/结构,您应该可以。我不认为在位字段之间添加填充,是吗?否则会有点破坏位域的意义。@JonathanPotter如果位域的总和不等于8的倍数,编译器将不会跨字节边界分割后续的整数等,因此它将填充。@MartinJames:对,但不会在位域之间。但这是一个依赖于平台的编译器扩展(即使gcc和VS使用相同的语法,也不知道)命令行选项绝对是VS。我将把这个帖子标记为答案,因为它不仅回答了问题,而且指出了位字段存储在C++中没有定义,也没有小的EnDad问题。实际上,在考虑EnDad问题之后,我不想选择这个解析流来解析。