在C

时间:2019-08-06 06:27:15

标签: c struct wireshark bit-fields struct-member-alignment

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

wireshark

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

这是原始标题

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;
};

这是wireshark的前32位输出

0000 0000 0010 0001 0000 0100 0000 1010 

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

2 个答案:

答案 0 :(得分:4)

“位应遵循结构中提到的顺序”

什么顺序? C语言不指定位顺序。您无法确定In [1]: import #import press-TAB Display all 631 possibilities? (y or n) ANSI audiodev markupbase AptUrl audioop markupsafe ArgImagePlugin avahi marshal BaseHTTPServer axi math Bastion base64 md5 BdfFontFile bdb mhlib BmpImagePlugin binascii mimetools BufrStubImagePlugin binhex mimetypes CDDB bisect mimify CDROM bonobo mmap CGIHTTPServer brlapi mmkeys Canvas bsddb modulefinder CommandNotFound butterfly multifile ConfigParser bz2 multiprocessing ContainerIO cPickle musicbrainz2 Cookie cProfile mutagen Crypto cStringIO mutex CurImagePlugin cairo mx DLFCN calendar netrc DcxImagePlugin cdrom new Dialog cgi nis DiscID cgitb nntplib DistUpgrade checkbox ntpath 是MSB还是LSB。

“在结构成员之间添加了额外的位”

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

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

在其中添加字节序,您会遇到麻烦-在您的情况下,CPU字节序和网络字节序都将很重要。

所有这些问题的最佳解决方案是完全丢弃位域。相反,您应该使用按位运算符,它们的定义,确定性和可移植性要好得多:

preamble

答案 1 :(得分:3)

位域的布局完全取决于实现。但是,通常一个单元无法越过存储单元边界,因此在这种情况下,它取决于uint32_t的大小。同样取决于实现方式,uint32_t mac_addr[6];的定义方式以及与其他内容的比较方式,以及uint32_t : 8是否可以覆盖unsigned char/uint8_t中的其余存储单元。

总而言之,它不会很漂亮。最好的做法是在结构中使用命名的{{1}}字段和数组,并将较小的成员按位缠绕到其中。