C++符号拓展带来的问题
符号扩展是指在将较小位宽的有符号整数转换为较大位宽的有符号整数时,保持数值符号不变的过程。符号扩展通常通过在高位填充符号位的值来实现。
符号扩展的规则如下:
- 如果最高位(符号位)是 0(表示正数),则在高位填充 0。
- 如果最高位是 1(表示负数),则在高位填充 1。
例如,假设一个 8 位的 char 类型val值为 0xFF,它在内存中的二进制表示是 11111111。这个值在 char 中表示 -1。如果我们将其转换为 32 位整数,符号扩展会将其转换为 11111111111111111111111111111111,这仍然是 -1 的二进制表示。
那么这就会导致一个问题,就是当发生unsigned和signed不同大小的整型类型之间的转换时,可能会出现结果不一致的问题,例如:
如下是一个一次性读取24位数值的函数:
uint32_t readU24(const char* p)
{
uint32_t value = 0;
value |= (p[0] << 16);
value |= (p[1] << 8);
value |= p[2];
return value;
}
当传入数据为char data[] = {0xFF, 0x01, 0x02};
时,最终输出的结果为 0xFFFF0102
,而不是预期的0xFF0102
,这是因为在执行value |= (p[0] << 16);
语句时,p[0]为0xFF,左移16位之后是0xFF0000
,在value对其进行或运算时,会由于char和uint32_t之间的类型发送符号拓展,填充高位的值’1’,那么执行完这条语句之后的value值则为:0xFFFF0000
。所以,value的值已经与预期结果不符了。
对应的解决方案有多种,这里就不写了,简单叙述:可以新建一个unsigned char的指针指向p,避免符号拓展,或者将参数p改为unsigned char*等。
所以,最好就是不要让不同整型(有无符号)之间的指针进行操作,保持统一,避免出现bug。