在 C 中的位域结构中添加额外的位

Extra bits added in bitfield struct in C

我正在尝试为 CAPWAP 协议创建客户端 C 代码。我尝试使用位域结构实现 CAPWAP header。但是在使用 sendto() 通过套接字发送这个结构之后,当我使用 wireshark 嗅探数据包时,我发现在它们之间添加了一些额外的位。我不知道这是从哪里来的。请求帮助。提前致谢。

wireshark

我尝试注释该结构的最后几个成员以使其 4 字节对齐。问题仍然存在。

这是原文header

struct cw_header
{
unsigned preamble : 8;
unsigned hlen : 5;
unsigned rid : 5;
unsigned wbid : 5;
unsigned t : 1;
unsigned f : 1;
unsigned l : 1;
unsigned w : 1;
unsigned m : 1;
unsigned k : 1;
unsigned flags : 3;
unsigned fragment_id : 16;
unsigned fragment_offset : 13;
unsigned reserved : 3;
uint32_t mac_length : 8;
uint32_t mac_addr[6];
uint32_t padding : 8;
};

我试着评论这些

//uint32_t mac_length : 8;
//uint32_t mac_addr[6];
//uint32_t padding : 8;

这是填充结构的地方

struct cw_header create_cw_header()
{
struct cw_header cw_header;
cw_header.preamble = 0;
cw_header.hlen = 1;
cw_header.rid = 1;
cw_header.wbid = 1; 
cw_header.t = 0;
cw_header.f = 0;
cw_header.l = 1;
cw_header.w = 0;
cw_header.m = 1;
cw_header.k = 0;
cw_header.flags = 0;    
cw_header.fragment_id = 0;  ;
cw_header.fragment_offset = 0;  
cw_header.reserved = 0; 
cw_header.mac_length = 6;
get_mac_address(cw_header.mac_addr);
cw_header.padding = 0;
return cw_header;
};

这是前 32 位的 wireshark 输出

0000 0000 0010 0001 0000 0100 0000 1010 

预期结果:位应该按照结构中提到的顺序 错误:在结构成员之间添加了额外的位

位域的布局完全依赖于实现。然而,一个单元不能跨越存储单元边界是很常见的,所以在这种情况下,它取决于 unsigned 的大小。 uint32_t 的定义方式以及它与其他所有内容的比较方式以及 uint32_t mac_addr[6]; 是否可以覆盖 uint32_t : 8.

中的其余存储单元也取决于实现。

总而言之,它不会很漂亮。最好的做法是在结构中使用命名的 unsigned char/uint8_t 字段和数组,并将较小的成员位组合到这些字段和数组中。

"Bits should be in the order mentioned in the struct"

什么顺序? C 语言没有指定位的顺序。您无法知道 preamble 是 MSB 还是 LSB。

"Extra bits are added in between struct members"

是的,编译器可以在位域的成员之间自由放置填充位或填充字节。位域被划分为抽象的 "invisible" 单元,标准称为 "storage unit",它们具有给定的大小——通常与 CPU 对齐的大小相同,但不一定如此。在不同的此类存储单元之间可能有也可能没有填充。编译器还可以自由地将不适合一个存储单元的位放入下一个存储单元。

如果不阅读编译器对位域的实现,就无法知道特定编译器会做什么。 C 标准几乎不支持它们。

在此基础上添加字节顺序,您会遇到一团糟 - 在您的情况下,CPU 字节顺序和网络字节顺序都很重要。

所有这些问题的最佳解决方案是完全删除位域。相反,您应该使用定义更明确、确定性和可移植性更好的按位运算符:

uint32_t cw_preable = something << preamble_offset;
...
uint32_t cw_header = 0;
cw_header |= cw_preamble;
...