二进制和位运算
1.有符号数和无符号数
有符号数
有符号数最高位表示符号位,最高位为0表示非负数(注意:最高位为0不一定表示正数,还可能表示0),最高位为1表示负数.
int x=1;//x为有符号数,在内存中表示为0x00000001,最高位为0
int y=-1;//y为有符号数,在内存中表示为0xffffffff,最高位为1
无符号数
无符号数所有位都表示数值,无符号数均为非负数
unsigned int num = 0B11111111'11111111'11111111'11111111;
cout << num << endl;//4294967295
2.负数的补码与二进制表示
有符号数在计算机中以补码的形式存储,对于正数而言,将其转化为对应的二进制即为补码,对于负数而言,一种简单的补码计算方案如下:
- 获取负数绝对值的二进制表示
- 将获取到的二进制取反后加1
以-3为例:
- 获取绝对值的二进制表示:00000000’00000000’00000000’00000011
- 将该二进制表示按位取反后加上1:11111111’11111111’11111111’11111101
已知数据在内存中的二进制表示,获取数据的十进制解释
如果数据在内存中二进制表示形式的最高位为0,按照一般正数的逻辑进行解释,如果最高位为1,表示该数据为负数,然后将二进制表示取反后在加1得到数值大小,例如数据在内存中显示为11111111’11111111’11111111’11111101,按照规则将其解释为-3
3.打印一个数在内存中的二进制表示
利用与运算的特点,可以判断一个数的某一位是否为0
void printBinary(int num) {
for (int i = 31; i >= 0; i--) {
cout << (((1 << i) & num) ? '1' : '0');
}
cout << endl;
}
通过二进制和16进制直接定义一个数
通过二进制或16进制定义数时,如何进行定义底层就会如何进行存储,不会做转化
int a = 0b10101101'00000000'01010101'01110011;
printBinary(a);//10101101000000000101010101110011
int b = 0xffffffff;
printBinary(b);//11111111111111111111111111111111
4.常见的位运算
- 与运算(&):2位均为真,结果才为真
- 或运算(|):任意1位为真,结果为真
- 异或运算(^):符号不同为真,可以理解为无进位相加
- 按位取反(~):0变1,1变0
- 左移(<<):左移时低位补0
- 有符号右移(>>):高位补符号位
- 无符号右移(>>>):高位补0,在Java中,存在无符号右移,C/C++中只有有符号右移
&、&&、|、||的区别
&&和||没有"穿透性",在进行逻辑与判断时,只要左边为false,右边便不在进行计算,在进行逻辑或判断时,只要左边为true,右边便不在进行计算
bool returnTrue() {
cout << "returnTrue被执行" << endl;
return true;
}
bool returnFalse() {
cout << "returnFalse被执行" << endl;
return false;
}
returnTrue() || returnFalse();//returnTrue被执行
returnFalse() && returnTrue();//returnFalse被执行
&和|具备穿透性,&和|一定会将整个结果计算完成,因为其计算结果表示的含义不一定为bool
returnTrue() | returnFalse();
returnFalse() & returnTrue();
5.相反数
相反数与按位取反是不同的概念
int a=-b//完全等价于int a=~b+1
INT_MIN的相反数还是自己,因为~INT_MIN+1还是自身
6.负数的设计
负数的存储比正数复杂许多,负数采用这种设计可以保证加法运算的正确性,以-1±1为例:
printBinary(-1);//11111111111111111111111111111111
printBinary(-2);//11111111111111111111111111111110
虽然在计算过程中出现了溢出,但是因为负数巧妙的设计,计算结果依然是正确的,使用正数加上负数时也有这样的效果,虽然计算过程中可能出现溢出,但是只要保证自己的调用得到的结果不溢出,计算机依旧可以正确的计算出来