BUG调试记录:C++中有符号类型和无符号类型在自动补位时的差异
昨天和同事一起干活的过程中,项目中一个函数的计算结果出现了问题,即使我们反复对照流程图并进行了一系列的手算也没有发现任何编码上的差错,在逻辑上整个函数的编写是完全没问题的,因此我们开始直接单步,并最终定位到了问题的所在,其原因为C++中对于无符号数和有符号数进行自动补位时的不同处理,接下来我结合代码详细说明一下这个问题。
错误代码
char a = 0xaa;
short b = 0;
b = b | a;
std::cout << std::hex<< b << std::endl;
解释:代码作者(不是我)写了一个或运算,在单步调试的时候作者发现只要过了这个或运算,数据就一定会出错。上边的代码就是我还原的他当时所写的代码的关键部分。
如上边的代码所示,他希望将一个数字“0xaa”和0作或运算,结果仍然是0xaa,在经常作位运算操作的人的意识海中,一个数字和0做或运算一定还是其本身,这是一种思维惯性,但是你可以尝试运行上边的代码,运算结果成为了一个非常大的数字,这里问题的关键就在于,进行运算的两个数字类型不同,因此在计算的时候存在低精度类型数字补位的行为,因此,在位运算中,低精度类型数字如何补位,便成为了问题关键。我们运行这个代码,能够发现结果如下:
结果为0xffaa,因为变量b是精度高的一方,因此无需补位,因此只能是因为a在补位的时候,高位并为如预想的那样补0,而是补的1,因此补成2字节之后,它成为了0xffaa,现在我们来验证一下:
short b = 0xaa00;
b = b >> 8;
std::cout << std::hex<< b << std::endl;
运行结果是0xffaa,至此我们可以认为在C++中,有符号数字在补位或者移位的时候,如果高位出现空缺,是直接补1的,那么是否和java一样,无符号数字的补位或者移位是补0呢:
unsigned char a = 0xaa;
short b = 0;
b = b | a;
std::cout << std::hex<< b << std::endl;
运行结果如图:
果然如预想的一样,接下来我们尝试一下移位:
unsigned short b = 0xaa00;
b = b >> 8;
std::cout << std::hex<< b << std::endl;
运行结果如下:
结论
在C++中,变量类型存在有符号和无符号两种大类,这两大类变量类型中的整形变量在补位或者移位的时候,如果出现高位空缺需要补位的情况,如果是有符号数字,则补1,如果是无符号数字,则补0。
这个问题可能在C++中是一个非常简单常见的问题,但由于本人是java出身,在java的变量类型中不存在无符号的概念,但是为了弥补这一点,在java的数字移位操作中,存在一个无符号移动和有符号移动,和C++中的不同类型变量的移动,补位不同表征相似。
尽管这是一个小问题,并且在大多数面向需求的开发中,计算操作并不是很多,但是这种小的问题确实需要注意。