解释一下为什么unsigned char在进行取反操作的时候会得出一个和你以为的不同的数字
比如~0xA5结果“应该”是2,但是计算机显示的却是250
int main(){ unsigned char a=0xA5,b; b=~a>>5; printf("%u ",b); }
假定该环境下的寄存器为16位
现在规定unsigned char的值为0xa5,也就是165,将a先取反再向右移5,得出的结果不是2而是250
错误思路:
unsigned char占一个字节,即8bit,将a写入内存中的形式为:
1010 0101
按照常规来说,将a取反为
0101 1010
然后再右移动5个为
0000 0010
结果为2。
但是这里特别要注意一点,低于int类型(即规定的寄存器大小)的基础类型需要在运算过程中转化为int类型(寄存器的位数)的大小进行计算,然后在运算完成以后再截取成原大小进行结果的表示,所以这里的过程应该改为:
正确思路:
1010 0101
在16bit的寄存器中转化为
0000 0000 1010 0101
全部取反得到
1111 1111 0101 1010
而后右向移动5个,左边补0:
0000 0111 1111 1010
运算完成,开始从右边截取出unsigned char对应位数的值
1111 1010
该数值为内存中对应数字的补码形式,但是由于是unsigned型,所以相当于可以直接取用为原码形式:
即250
这里补充一个取反操作的知识点,我们在学习取反~这个操作
初学者在没有了解内存是如何存储数据的情况下往往会发现
取反之后读取内存中的数字有的时候是需要对内存中的数字取反加一再补负号
而有一些则直接取出当成结果就好,这是为什么呢?
1.一个数字存入内存中的时候,不是直接转为二进制直接存储的,编译器需要把当前数字转化为其对应的补码存入内存当中:正数的补码就是其原码(即其本身),而负数则需要对其取绝对值再加一。
例1.
-5(char型)转化为补码的方式为首先转为绝对值:
0000 0101 接着取反
1111 1010 再加1
1111 1011 这就是-5在内存中的表现形式
例2.
5的原码为0000 0101,其补码也为0000 0101
2.在内存中,若当前数字的补码最高位为1:表示这个数字为一个负数;需要取出这个数字的时候就需要首先对这个数字进行取反然后加一再补负号还原这个数字。
在内存中,若当前数字的最高位为0:则这个数字表示一个正数,不需要转化可以直接取出来使用
例1.
需要把-5这个数字从内存中取出
1111 1011 首先取反
0000 0100 然后加一
0000 0101表示5,再补符号
-5 取出成功
例2.
在内存中取出5这个数字:
0000 0101
直接取出的到5即可
3.如何判断当前数字在取反后从内存取出时应不应该执行取反加一再加符号的操作:根据上面两条,由于取出数字是在内存当中执行的,所以我们要在取出的时候查看当前数字最高位是否是1,若发现当前数字在完成取反操作以后结果最高位为1是负数,则需要取反加一,反之不需要。
例1.
-5在内存当中表示
1111 1011 进行取反操作得到
0000 0100 编译器判断当前数字取反以后变成了正数,在取出这个数字时就不需哟取反加一了
结果就是4
例2.
5在内存中表示为
0000 0101 取反操作
1111 1010 编译器判断高位为1是负数,则需要取反加一补符号
0000 0101 先取反然后再加1
0000 0110的到结果6再补符号
~5的结果为-6
====================================================================================