不管是学习什么编程语言都会遇到各种运算符,运算符主要分为以下 6 类:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、其他运算符。今天我们主要看一下位运算符中的取反运算符(~),毕竟这个运算符可是会变戏法。
取反运算符
取反运算符(~),因为是位运算符,所以只能够给整数、布尔值和字符进行运算。我们先来试试简单的布尔值做取反运算是不是和我们想的一样(真取反得到假,假取反得到真)?大家可以先猜一下结果再看下面的图。
~True 的运算结果是 -1,为啥不是 0 或者 False?~False 的运算结果是 -2,为啥不是 1 或者 True?取反运算符果然会变戏法。至于这个戏法怎么变的我们先不管,我们把 True 改成 1,False 改成 0,看看结果是不是和上面一样,如图所示。
果然一模一样,看样子之前的布尔值也是先隐式转换为对应的整数值再做运算的!接下来解决今天的重点问题,为什么 0 取反后变成 -1,1 取反后变成 -2?这个问题我选择使用 C 语言进行讲解,因为 C 语言相对底层。我们先看一下下面这个程序:
代码语言:javascript复制 #include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 0;
printf("%d,%xn", a, a);
system("pause");
printf("%d,%xn", ~a, ~a);
}
代码比较简单,重点是两个字符串格式化:%d 表示以十进制格式输出,%x 表示以十六进制格式输出。运算结果猜对前 3 个是没有问题的,第 4 个有些难。运行结果先看一下前面两个输出,如图所示。
输出结果没什么好解释的,毕竟 0 的十六进制还是 0,下面两个输出大部分人会猜测 -1,80000001,毕竟整数在 32 位计算机程序中占 4B 内存,-1 转成二进制是 1000 0000 0000 0000 0000 0000 0000 0001(其中最高位是符号位,1 表示负数),然后把每 4 位合并成一个十六进制就得到了 80000001,不管这个结果是对还是错我们来看一下正确答案,如图所示。
最后一个十六进制输出有些奇怪,结果和我们所想的完全不一样,该不会又是取反运算符的变戏法吧?其实这里的输出和取反运算符一点关系都没有,在这里真正变戏法的是计算机本身,下面我们就来详细讲解。
数据在计算机中的存放形式
从上面的输出我们可以发现一个问题,-1 在计算机中并不是转换为二进制直接存储的,而是做了手脚。我们先写一下 80000001 和 ffffffff 两个十六进制数的二进制:
1000 0000 0000 0000 0000 0000 0000 0001
1111 1111 1111 1111 1111 1111 1111 1111
从上面一串二进制码转换为下面一串二进制码的逻辑看一下就知道了,把除去符号位,其余每一位 1 换成 0,0 换成 1,然后整体 1 就完事了,这不就是负数的原码转换成补码的过程吗?看样子数据在计算机中存放形式已经水落石出了,正数转二进制直接存储(存储源码),负数存储其补码。
总结
取反运算符运算逻辑确实是转成二进制的机器数后 1 变成 0,0 变成 1(符号位也要变),然后结果给出其十进制对应的真实数就完事了。