位运算
首先我们来了解一下什么是位运算,从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态,计算机对二进制数据进行的运算(+、-、*、/)都是叫位运算,即将符号位共同参与运算的运算。位运算是把运算对象看作是由二进位组成的位串信息,按位完成指定的运算,得到位串信息的结果。位运算的运算分量只能是整型或字符型数据。
只是这样描述的话有点抽象,那我们来举一个例子看一下CPU是如何去运算的:
int a = 25 ; int b = 43 ; int c = a + b ;
计算两个数的和,在计算机中是以二进制来进行运算,所以上面我们所给的 int 变量会在机器内部先转换为二进制在进行相加:
25: 0 0 0 1 1 0 0 1
43: 0 0 1 0 1 0 1 1
————————————————————
68: 0 1 0 0 0 1 0 0
所以,相比在代码中直接使用(+、-、*、/)运算符,合理的运用位运算更能显著提高代码在机器上的执行效率。
位运算符
位运算符用于处理数据的位运算 。
在我们今天所学习的运算符中,位运算符有: &(按位与)、|(按位或)、^(按位异或)、~ (按位取反)。其中,只有 ~ (按位取反) 运算符是单目运算符,其余均为双目运算符。
优先级
位运算符的优先级从高到低,依次为 ~、<<、>>、&(按位与)、^、| ;
其中~的结合方向自右至左,且优先级高于算术运算符,其余运算符的结合方向都是自左至右,且优先级低于关系运算符。
按位与运算符(&)
定义:参加运算的两个数据,按二进制位进行"与"运算。
按位与运算将两个运算分量的对应位按位遵照以下规则进行计算:
0&0=0 0&1=0 1&0=0 1&1=1
当两边同时为1时,结果才为1,否则的话结果为0。也就是,全真为真,一假则假。
注意:这里是按二进制位进行运算,所以如果是 3&5 即 0000 0011& 0000 0101 = 0000 0001,因此 3&5 的值得1。并且负数将按补码形式参加按位与运算。
与运算的典型用法:
1. 取一个数的指定位 比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。
2.判断奇偶 二进制中,我们可以根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。所以我们可以用if ((a & 1) == 0) 代替 if (a % 2 == 0) 来判断a是不是偶数。
3.清零 如果想将一个单元清零,就只需使其全部二进制位为0就可以了,这时只要与一个各位都为零的数值相与,结果为零。
按位或运算符(|)
定义:参加运算的两个对象,按二进制位进行"或"运算。
按位或运算将两个运算分量的对应位按位遵照以下规则进行计算:
0|0=0 0|1=1 1|0=1 1|1=1
参加运算的两个对象只要有一个为1,其值为1;如果全为0,其值就为0 .也就是,一真全真,全假才假。
注意:这里是按二进制位进行运算,所以如果是3|5就是 0000 0011| 0000 0101 = 0000 0111,因此,3|5的值得7。并且负数按补码形式参加按位或运算。
或运算的典型用法:
或运算通常用来对一个数据的某些位设置为1,如将要将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后进行按位或运算(X|Y=1010 1111)即可得到。
按位异或运算符(^)
定义:参加运算的两个数据,按二进制位进行"异或"运算。
按位异或运算将两个运算分量的对应位按位遵照以下规则进行计算:
0^0=0 0^1=1 1^0=1 1^1=0
参加运算的两个对象只要相对应,其值为0;否则的话,其值就为1 .也就是,相应位的值相同为 0,不相同为 1。
注意:这里是按二进制位进行运算,所以如果是3^5就是 0000 0011^0000 0101 = 0000 0110,因此,3^5的值得6。
按位异或运算满足以下性质:
1.交换律:a^b = b^a ;
2.结合律: (a^b)^c == a^(b^c) ;
3.自反性: a^b^b = a^0 = a ;
4.自身与0性: 任意一个数N,都有N^0 = N ; N^N = 0 ;
按位异或运算的典型用法:
1.翻转指定位 比如如果我们要将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。
2.交换两个数
void my_Swap(int &a, int &b) { if (a != b) { a ^= b ; //在这里 a^-b 等价于 a = a^b b ^= a ; a ^= b ; } } /*
举例:
如果a = 3, b = 5 ;
a 的二进制表达: 0000 0011
b 的二进制表达: 0000 0101
a^b = 0000 0110
a = 0000 0110
b^a = 0000 0011
b = 0000 0011
a^b = 0000 0101
a = 0000 0101
这样就完成了反转
*/
按位取反运算符(~)
定义:参加运算的一个数据,按二进制进行"取反"运算。
按位取反算将两个运算分量的对应位按位遵照以下规则进行计算:
~1=0 ~0=1
注意:按位取反运算是单目运算,用来求一个位串信息按位的反,即哪些为0的位,结果是1;而哪些为1的位,结果是0。也就是0变1,1变0 。
按位取反运算的典型用法:
可以使一个数的最低位为零 如果我们想要使a的最低位为0,可以表示为:a & ~1。~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0。
左移运算符(<<)
定义:将一个运算对象的各二进制位全部左移若干位(最左边的二进制位丢弃,右边补0)。
就比如 a=1010 1110,a = a<< 2 将a的二进制位左移2位,左移时,空出的右端用0补充,左端移出的位的信息就被丢弃。即得a=1011 1000。
在二进制数运算中,在信息没有因移动而丢失的情况下,每左移1位相当于乘2。例如14<<2,结果为0001 1100,也就是14*2 = 28 。
右移运算符(>>)
定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。右端移出的位的信息被丢弃
在右移时,需要注意符号位问题。对无符号数据,右移时,左端空出的位用0补充。对于带符号的数据,如果移位前符号位为0(正数),则左端也是用0补充;如果移位前符号位为1(负数),则左端用0或用1补充,取决于计算机系统。对于负数右移,称用0 补充的系统为“逻辑右移”,用1补充的系统为“算术右移”。
操作数每右移一位,相当于该数除以2。也就是 x>>n = x/(2^n) ;
总结
我们再此列出一张表来总结我们上面所学习的位运算符:
逻辑运算符
逻辑运算符可以将两个或多个关系表达式连接成一个或使表达式的逻辑反转。C 语言支持的所有关系逻辑运算符有三个分别是:&&(逻辑与运算符) 、||(逻辑或运算符) 、!(逻辑非运算符)。
优先级
&&、|| 和 !的优先级为:
! > && > ||
当然,虽然运算符有着优先级的设定,但是在平时编写的时候,还是会建议使用括号去表明先后顺序,这样也更容易避免一些错误。
逻辑与运算符(&&)
&& 运算符被称为逻辑与运算符。它需要两个表达式作为操作数,并创建一个表达式,只有当两个子表达式都为 true 时,该表达式才为 true。也就是我们两边同真为真,一假则假。
举例:
if ((n <20) && (m > 12)) { printf("此时n<20 且 m>12") ; }
上述例子,n小于20时左侧为真,m>12时右侧为真,所以只有左右两侧均满足条件时,才会执行if语句,否则的话我们将不会执行。
逻辑或运算符(||)
|| 运算符被称为逻辑或运算符。它需要两个表达式作为操作数,并创建一个表达式,当任何一个子表达式为 true 时,该表达式为 true。也就是我们两边同假为假,一真则真。
举例:
if ((n <20) || (m > 12)) { printf("此时n<20 且 m>12") ; }
上述例子,n小于20时左侧为真,m>12时右侧为真,这时只有我们左右两侧同时不满足为真的条件时,我们才不会执行这个语句;否则的话,不论哪个为真,都要执行if语句。
逻辑非运算符(!)
! 运算符被称为逻辑非运算符,执行逻辑 NOT 操作。它可以反转一个操作数的真值或假值。换句话说,如果表达式为 true,那么 ! 运算符将返回 false,如果表达式为 false,则返回 true。也就是非真即假,非假即真。
举例:
if (!(n <20)) { printf("此时n>=20") ; }
上述例子中,如果我们不加 "!" 号,则当n<20的时候才可以执行if语句,但是如果我们运用逻辑非运算符后,就变成了,当n>=20时才会执行该语句,条件就反了过来。
总结