工作中总是会遇到不同类型之间数据相互赋值,有的时候影响不大,有的时候可能会出现出乎意料的结果,作者就曾经遇到过数据溢出的问题,之前没有仔细研究分析,现在对这部分内容做一个总结。
首先抛出一个问题,将 -1 赋值给有符号的变量和无符号的变量,该变量在内存中的数据是否是一样的?
uint8 i = -1;
int8 ii = -1;
大家都知道负数在内存中的存储方式是以补码的方式存储的。-1为例子,最高位为1,表示负数。
即原码为:1000 0001,反码为 1111 1110,所谓反码就是符号位不变,其他位取反。补码为反码+1 即 1111 1111.如果将 -1 分别赋值给有符号和无符号的变量会发生什么?其实负数存储方式在计算机中都是补码的方式,只要将负数赋值给变量,这个变量中所在的内存数据就是该负数的补码数据。只是有符号和无符号解析这个数据的方式不一样,无符号把最高位当数据位,而有符号把最高位当做符号位解析而已。下面看调试结果。
实际调试发现,只要是对变量赋值-1,无论变量是有符号还是无符号,内存中的数据都是ff,知识无符号的解析成255,有符号的解析成1.
除了将无符号的变量赋值负数的异常使用外,还有以下更为常见的异常使用方式。
1,将长整型赋值给短整型。
这个比较好理解,相当于把一个大的数据放到一个小的盒子中,肯定装不下,只能截取。实际上是截取保留的低位。同样看个实际的例子。结合上面举个比较极端例子,将一个有符号长整形赋值一个-2,并将他赋值一个短的无符号的变量。
int16 i = -2;
uint8 ii = 0;
ii = i;
来分析下ii变量的值。i 内存中的数据为fe(-2 源码 1000 0000 0000 0010 反码 1111 1111 1111 1101 补码为 1111 1111 1111 1110),赋值给ii后,截取低位1111 1110,然后解析成无符号的数据就是254,是不是有点意外?
这个其实是跟把一个超过类型范围内的数据赋给某个变量是一样的效果,只截取低位,比如
uint8 ii = 257; 0001 0000 0001 ii值为1。
2,将短整形数据赋值为长整形数据。
这个也好理解,将小数据放在大空间中,肯定需要补数,正常情况下,(有符号赋值有符号或者无符号赋值无符号)因为短整数表示的数据,长整形也能表示,所以赋值后的数据肯定是不能变的,也就是把短整形的符号位扩展。
int16 i = -2; 1111 1111 1111 1110
int8 ii = 1; 0000 00001
ii = i; 0000 0000 0000 0001 赋值后ii变成1
int16 i = -2; 1111 1111 1111 1110
int8 ii = -1; 11111 1111
ii = i; 1111 1111 1111 1111 赋值后ii变成-1,
说白了,正常情况下的短数据赋值长数据,值不变,只是占得字节数不一样了
看一个特殊的例子,将-1赋值无符号类型,然后赋值给有符号的长整形。
int16 i = -2; 1111 1111 1111 1110
uint8 ii = -1; 11111 1111 //这个值按照无符号数据解析出的数据其实是255
ii = i; 0000 0000 1111 1111 赋值后ii变成0x00FF,也是255,保持数据不变
uint16 i = -2; 1111 1111 1111 1110
int8 ii = -1; 11111 1111 //实际值是-1
ii = i; 1111 1111 1111 1111 赋值后ii变成0xFFFF,还是-1,
所以对于有符号和无符号之间的数据相互赋值,需要补高位,补0还是补1,需要看补什么数据是保持实际值不变,这个是最终目标。
异常赋值最隐蔽的有可能发生在数据相乘之中,不小心就可能出现溢出:
160*2=320 // 0001 0100 0000 -->截取0100 0000 为64