C++-最近看到一道二进制反码的问题,挺有趣
题目:
#include <stdio.h>
int main(void){
unsigned char a = 0xa5;
printf("%dn",~a);
char b = ~a;
printf("%dn",b);
unsigned char c = ~a;
printf("%dn",c);
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
查看一下它的汇编代码就知道了,它是先进行扩展然后取反的。
0xA5展开后是0x000000a5,取反是0xffffff5a
然后printf出场了,它看到这个0xffffff5a认为是有符号值,那么如果要输出就要取反+1,
在内存中0xffffff5是补码,输出负数就要取反+1,取反时保留最高位
那么就是1000 0000 0000 0000 0000 0000 1010 0101 再加+1
1000 0000 0000 0000 0000 0000 1010 0110 即-166
应该是这样的。
printf的%d默认会把传进来的参数转换成int。
0xa5到四字节的int就是0x000000a5,直接在printf的参数里取反就是0xffffff5a。0xffffff5a按有符号整数解释的绝对值就是~0xffffff5a+1 = 0x000000a6 = 116,所以第一行就输出-116了。
后面两个90是因为转换后还是存在1字节的char里,前面的那些位被截断,所以就剩0x5a = 90
不管哪种情况,第一步都是对0xA5 取反,注意取反的时候是按照32位做的,得到结果0xffffff5a,
第一种:按照int型解释的10进制即为-166
第二种:将0xffffff5a赋值给char b,此时b = 0x5a,将b扩展为int即为0x0000005a,即为90
第三种与第二种的区别即为将char b 和unsigned char扩展为int的区别,该题中为0xa5,首位为0,没看出区别,如果将0xa5换成0x25,你将发现三个结果都不相同
~ 这是按位求反的0xa5的二进制是10100101,十进制就是165,但是输出的是~a那么就要按位求反之后再+1,就成-166了,至于b和c 因为char和unsign char 的区间分别是-128~127,0~256,所以输出之后b,c都为-166+256则为90。
C/C++中char和unsigned char的数值运算都会先展开为int或unsigned int
32位系统中,0xA5展开后是0x000000a5,取反是0xffffff5a。用printf的%d会将这个值认为是有符号的数,即-166。
第二个由于先赋值给char类型,所以展开后是0xffffffa5,取反是0x5a,即90
第三个0xA5取反后是0xffffff5a,赋值给unsigned char后截去高位是0x5a,即90
《深入理解计算机系统(原书第2版)》
第二章里面有详细解释。
printf的%d默认会把传进来的参数转换成int。
0xa5到四字节的int就是0x000000a5,直接在printf的参数里取反就是0xffffff5a。
0xa5对应的十进制为165,它的反码为-166.
第一次输出是以十进制输出a的反码,即为-166.
第二次将a的反码先存到字符型变量b中,char能表示-128~127,而~a为-166,所以b=-166+256,b为90,输出90。
同理,第三次将a的反码先存到无符号字符型变量c中,unsigned char能表示0~255,而~a为-166,所以c=-166+256,c为90,输出90。
第一次之所以与后两次不同是因为~a并没有先存入其他变量,而是直接输出,其值并没有在赋值时发生改变。