uint16_t转自定义位域类异常:取值与类大小不符预期
一、强制转换的本质错误
你用(test)tci做强制转换时,并没有把tci的二进制位直接映射到test类的位域成员中。因为test类定义了自定义构造函数,这个C风格强制转换实际是调用了test(uint16_t vid, uint8_t pcp=0, bool dei=0)构造函数——把tci的值传给了vid参数,pcp和dei则使用默认的0值。这就是为什么实际输出里pcp显示为空(本质是0,作为uint8_t输出时是不可见控制字符)、dei为0,只有vid是4095(65535的低12位正好是4095)。
如果你想将tci的二进制位直接解析为test的位域结构,不能依赖构造函数的默认初始化逻辑,得用位运算手动提取或者内存拷贝(后者需谨慎)。
二、sizeof(test)为2的合理性
你认为sizeof(test)应该是4,核心是误解了位域的定义规则:
- 位域冒号后的数字是比特数(bit),不是字节数(byte),你代码里的注释“3 bytes”“12 bytes”都是错误的。
test类的位域总比特数是3+1+12=16位,刚好等于2字节。编译器会尽可能紧凑地打包位域:即便三个位域的底层类型分别是uint8_t、bool、uint16_t,编译器也会将它们合并到连续的2字节内存中,所以sizeof(t)返回2是完全正确的。
三、正确实现方式
方法1:位运算手动解析(推荐)
新增一个从uint16_t直接构造的构造函数,手动提取TCI的各个字段(符合802.1Q标准的TCI结构:高3位PCP,第13位DEI,低12位VID):
#include <iostream> #include <cstdint> using namespace std; class test{ public: uint8_t m_pcp : 3; // 占3比特 bool m_dei : 1; // 占1比特 uint16_t m_vid : 12;// 占12比特 public: // 从TCI直接构造的构造函数 test(uint16_t tci) { m_pcp = (tci >> 13) & 0x7; // 提取高3位 m_dei = (tci >> 12) & 0x1; // 提取第13位(从0计数) m_vid = tci & 0xFFF; // 提取低12位 } // 保留原构造函数 test(uint16_t vid, uint8_t pcp=0, bool dei=0) { m_vid = vid; m_pcp = pcp; m_dei = dei; } }; int main() { uint16_t tci = 65535; // 二进制:111 1 111111111111 test t(tci); // 注意:m_pcp是uint8_t位域,直接输出可能显示为ASCII控制字符,转int后输出数字 cout<<"pcp: "<<(int)t.m_pcp<<" dei: "<<t.m_dei<<" vid "<<t.m_vid<<"\n"; return 0; }
运行后输出为:pcp:7 dei:1 vid 4095——这里和你预期的pcp:1不符,应该是你对TCI的结构理解有误,65535的高3位是111(即7),如果要得到pcp=1,需要将tci设为(1<<13)|(1<<12)|4095= 8192+4096+4095=16383。
方法2:内存拷贝(依赖编译器布局)
如果要直接将tci的二进制位映射到位域,可使用memcpy,但需注意编译器的位域布局规则(比如位是从高位到低位还是低位到高位分配):
#include <iostream> #include <cstdint> #include <cstring> using namespace std; class test{ public: uint8_t m_pcp : 3; bool m_dei : 1; uint16_t m_vid : 12; public: test() = default; // 默认构造函数 test(uint16_t vid, uint8_t pcp=0, bool dei=0) { m_vid = vid; m_pcp = pcp; m_dei = dei; } }; int main() { uint16_t tci = 65535; test t; memcpy(&t, &tci, sizeof(t)); cout<<"pcp: "<<(int)t.m_pcp<<" dei: "<<t.m_dei<<" vid "<<t.m_vid<<"\n"; return 0; }
这个方法的风险在于,不同编译器的位域布局可能不同,跨平台时可能出现解析错误,因此更推荐方法1。
内容的提问来源于stack exchange,提问作者rashi

