C++的运算符和C似乎都是一样的,运算的结合性和优先级都一样。但位运算,从学C的时候我就没有搞懂,看C++的书更是看不懂,于是在这里就做个小专题研究研究。
一、整型数据在内存中的存储方式
要更透彻了解位运算那些东西,我最好还是先来复习(学习)一下整型数据在内存中的存储方式,这部分很多内容来自于C语言中文网的C语言整型数据(整数)教程。
整型数据在内存中的存放形式
如果定义了一个整型变量i:
int i; i=10;数值是以补码表示的:
- 正数的补码、反码和原码相同;
- 负数的补码:将该数的绝对值的二进制形式按位取反再加1。
例如:求-10的补码:
由此可知,左面的第一位是表示符号的。
各种无符号整型数据所占的内存空间字节数与相应的有符号类型量相同。但由于省去了符号位,故不能表示负数。
二、位运算符和移位(此部分内容来自和HappyCtest修改自维基百科位操作词条)
位运算符
1.取反(NOT) ~
取反是一元运算符,对一个二进制数的每一位执行逻辑反操作。使数字1成为0,0成为1。例如:
NOT 0111(十进制7) = 1000(十进制8)许多程序设计语言(包括C程序设计语言family),取反操作符用波浪线”
~
“表示。值得注意的是此操作符与”逻辑非(!
)”操作符不同。在C++中,逻辑非将数字整体看做一个布尔类型–将真值转化为假,将假值转化为真;而C语言将值1转化为0,将非零值转化为0。”逻辑非”并不是一个位操作。
但数据是以补码的形式储存在计算机里面的,所以计算机取反还要考虑补码、符号位吧啦吧啦的问题,示例:
65: //正数的原反补码都相同 原码:01000001 反码:01000001 补码:01000001 取反过程: 取反:10111110 //符号位为负了,要求它的补码:再一次取反(算补码的取反时符号位不变),最低位+1 取反:11000001 加一:11000010 最终结果:-66 即~65=-66 -65: //复数的补码等于其绝对值的原码取反+1 绝对值的原码:01000001 取反:10111110 补码:10111111 取反过程: 取反:01000000 最终结果:64 即~(-65)=64
晕死@~@!
2.按位或(OR) |
按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为1,该位的结果值为1。例如
0101(十进制5) OR 0011(十进制3) = 0111(十进制7)在C类程序设计语言中,按位或操作符是”|”。这一操作符需要与逻辑按位或运算符(||)区别开来。
按位或能够将每一位看做旗帜;在二进制数中的每一位可以表示不同的布尔变量。应用按位或操作可以将二进制数的某一位设为1。例如
0010(十进制2) 能够看做包含4个旗帜的组合。第1,2,4旗帜为0;第3个旗帜为1。利用按位或可以将第1个旗帜设置为1,而其他旗帜不变。 0010(十进制2) OR 1000(十进制8) = 1010(十进制10)这一技巧通常用来保存程序中的大量布尔变量。
于是我又来补充了:
计算机里面是补码进行运算,运算完后如果符号位是1,那要再去其补码(除符号位外取反再+1才是正确答案),例如:
65 原码:01000001 反码:01000001 补码:01000001 7 原码:00000111 反码:00000111 补码:00000111 -65: 绝原:01000001 反码:10111110 补码:10111111 65|7(补码参与运算): 01000111 最终结果为71 -65|7: 10111111 //符号位为1,再取反(符号位不变)+1一次才是补码: 取反:11000000 加一:11000001 最终结果为:-65
3.按位异或(XOR) ^
按位异或运算,对等长二进制模式按位或二进制数的每一位执行逻辑异按位或操作。操作的结果是如果某位不同则该位为1,否则该位为0。例如
0101 XOR 0011 = 0110在类C语言中,按位异或运算符是”
^
“。汇编语言的程序员们有时使用按位异或运算作为将寄存器的值设为0的捷径。用值的自身对其执行按位异或运算将得到0。并且在许多架构中,与直接加载0值并将它保存到寄存器相比,按位异或运算需要较少的中央处理单元时钟周期。
按位异或也可以用于在比特集合中切换旗帜。给出一个比特模式,
0010第一和第三位能够通过按位异或运算使用同时切换。
0010 XOR 1010 = 1000这一技巧可用于操作表示布尔变量的比特模式。
计算机里:
65 原码:01000001 反码:01000001 补码:01000001 7 原码:00000111 反码:00000111 补码:00000111 -65: 绝原:01000001 反码:10111110 补码:10111111 65^7: 01000110 最终结果:70 -65^7 10111000 //又是符号位为1的stuff,再来 取反:11000111 加一:11001000 最终结果:-72 :)我差不多会啦!
3.按位与(AND) &
按位与处理两个长度相同的二进制数,两个相应的二进位都为1,该位的结果值才为1,否则为0。例如:
0101 AND 0011 = 0001在类C语言中,按位与用’&’表示
计算机里:
65 原码:01000001 反码:01000001 补码:01000001 7 原码:00000111 反码:00000111 补码:00000111 -65: 绝原:01000001 反码:10111110 补码:10111111 65&7: 00000001 最终结果:1 -65&7: 00000111 最终结果:7
4.移位 >> <<
移位是一个二元运算符,用来将一个二进制数中的每一位全部都向一个方向移动指定位,溢出的部分将被舍弃,而空缺的部分填入一定的值。在类C语言中,左移使用两个小于符号”<<“表示,右移使用两个大于符号”>>”表示。
算术移位
Left arithmetic shift
150px arithmetic shift逻辑移位
应用逻辑移位时,移位后空缺的部分全部填0。
0001(十进制1) << 3(左移3位) = 1000(十进制8) 1010(十进制10) >> 2(右移2位) = 0010(十进制2)
咋一看是不是没有看懂逻辑和算数移位的不同,解答:
逻辑移位:不考虑正负号
算术移位:考虑正负号。
eg: 1000000000000000(右移2位) 0000000000000000(左移2位)
逻辑移位:0010000000000000 0000000000000000
算术移位:1110000000000000 1000000000000000
计算机至少在C类语言中,移位都是算数移位,考虑符号。左移用0补,游移看符号,若符号位为1则用1补。只是其实现在的计算机不止8位,所以其实一个二进制数有更多的位数。这暂时不探讨,之后更懂了再说。
终于搞定了@-@,yoho~!;)