前言
首先,我们要了解一个概念:程序中的所有数在计算机内存中都是以二进制的形式储存的。而位运算,就是直接对在内存中的二进制位进行操作,跳过了 程序转义成二进制的这一步骤,对编译时间有所提高,但带来的缺点也很明显,程序的可读性变低了。
掌握位运算 是一位程序员的基本素养,位运算在计算机领域的作用可谓举足轻重。
下面我将讲解 位运算的大体方法以及一些基本的应用。
正文
and 运算
只有对应的两个 二进制数,均为 1,结果才为 1,否则为 0
& 1
判断 n 的第 m 位数。
介绍两个重要应用,来证明其含义:
- (n >>m ) & 1 判断 整数 n 的二进制 第 m 位 是否 为 1 或者 为 0 n 的第 m 位数,如果为 1 ,1&1 就返回 1 ,如果为 0,0&1 就返回 0
- 判断 奇偶性 如果 n 为奇数,辗转相除 2,最后的余数 必定为 1,如果 n 为偶数,辗转相除 余数必定为 0 也就是说,n 为奇数,n&1 等价于 1&1 ,返回 1,n 为偶数,n&1 等价于 0&1,返回 0
& 0
将 n 的第 m 位数,重置为 0 基本应用:n & ~(1 << m)
1 << m 定位到 n 的第 m 位数
~(1 << m) 将 1 进行非运算,变为 0,其他剩下的 m 位,变成 1,而 &1,是无实际作用的
n & 0 :& 按位与运算 只有对应的两个数 全部为 1 时,结果才为 1,而&0,返回值一定为 0
or 运算
只有对应的两个 二进制数,均为 0,结果才为 0,否则为 1
| 1
将 n 的第 m 位数,重置为 1
基本应用:n | (1 << m):
1 << m
定位到 n 的第 m 位数n | 1
n 的第 m 位数 进行 |1 操作 其返回值必定为 1! 因为|只有,两个数都为 0 时,结果才为 0
| 0
无实际作用
一定要清楚, 是 n 的第 m 位数 在进行操作,其他 位数操作,根本无 影响
因为,其他位数,是 在进行 “| 0” 操作,而所谓的 |0 操作,与 &1 操作,毫无差别。
假设 k=n 的第 m 位数,k = 1 ,k|0 = 1|0 = 1,k = 0,k|0 = 0|0 = 0,所谓 无实际作用。
但是要注意一点,我说的 0 是在二进制数中的 0,有实际含义的 0,不是补 0 的 0
所以,可以感性的认识到,| 0 与 & 1 以及 下文的 ^ 0,都是无实际作用的
xor 运算
只有对应的两个 二进制数相等时,结果才为 0,否则为 1
^ 1
将 n 的第 m 位数,取反
基本应用:n ^ (1 << m)
1 << m : 定位到 n 的第 m 位数
n ^ 1,我们要知道,^ (异或):不相等为 1,相等为 0
而 ^1:如果 n 的第 m 位数 为 1,1^1 返回值为 0,如果 n 的第 m 位数 为 0,0^1 返回值 为 1
所以,^1 的重要作用,就是 与之相反的作用
^ 0
无实际作用
假定 整数 k 为 0,k^0 = 0^0 = 0 ; 假定整数 k 为 1,k^0 = 1^0 = 1
所以说,无论怎么变化,^0 都是无实际作用的
shl & shr 运算
- 左移运算符 “<<” 表达式:a << b a<<b 的值是:将 a 各二进位全部左移 b 位后得到的值。左移时,高位丢弃,低位补 0。 实际上,左移 1 位,就等于是乘以 2,左移 n 位,就等于是乘以 2n。 而左移操作比乘法操作快得多。
1
2
3
4
5例如:9 << 4
9的二进制形式:0000 0000 0000 0000 0000 0000 0000 1001
因此,表达式“9<<4”的值,就是将上面的二进制数左移4位,得:
0000 0000 0000 0000 0000 0000 1001 0000
即为十进制的144 , 而 9*2的4次幂 = 9*16 = 144. - 右移运算符 “>>” 表达式:a >> b a>>b 的值是:将 a 各二进位全部右移 b 位后得到的值。右移时,移出最右边的位就被丢弃。 对于有符号数,如 long,int,short,char 类型变量,在右移时,符号位(即最高位)将一起移动,并且大多数 C/C++编译器规定,如果原符号位为 1,则右移时高位就补充 1,原符号位为 0,则右移时高位就补充 0。
1
2实际上,右移 n 位,就相当于左操作数除以2n,并且将结果往小里取整。
例如:-25 >> 4 = -2 -2 >> 4 = -1 18 >> 4 = 1应用
对 2 的整数幂进行模运算
1
2
3
4
5
6
7
8
9
10
11
12
int main(){
int n,k;
while(~scanf("%d %d",&n,&k)){
n<<=k;//相当于 n 乘以 2 的 k 次幂,并将结果赋给 n
n>>=k;//相当于 n 除以 2 的 k 次幂,并将结果赋给 n
printf("%d\n",n);
}
return 0;
}两数交换
1
2
3
4
5
6
7
8
9
10
11
int main(){
int n,m;
while(~scanf("%d %d",&n,&m)){
n^=m;
m^=n;
n^=m;
printf("%d %d\n",n,m);
}
return 0;
}按位异或 ^ : 不相同 为:1 ; 相同 为 :0 将参与运算的两操作数各对应的二进制位进行异或操作, 即只有对应的两个二进位不相同时,结果的对应二进制位才是 1,否则为 0。
异或运算的特点是:如果 a^b=c,那么就有 c^b = a 以及 c^a=b
1 | 例如:表达式“21 ^ 18 ”的值是7(即二进制数111)。 |
层次结构:A->B B->A A->B 正 反 正
判断 2 的正整数幂
1 |
|
给定整数 n 判断 n 是否为 2 的正整数幂 表达式:(! (n & (n-1)) && n
1 | 举个例子: n = 16 = 10000,n-1 = 15 = 1111 |
是的,如果一个数 n 是 2 的正整数幂,那么 n 的二进制必定为 1000…. n-1 的二进制必定为 1111…. 即: n & n-1 = 0 所以 (! (n & (n-1)) 为 1 ; && n :判断 n 为正数
判断奇偶性
1 |
|
记住:在做位运算时,位数不够的数,自动在 前面补 0 比如:21 & 1 :10101 & 00001 = 00001 = 1 16 & 1 :10000 & 00001 = 00000 = 0 事实证明:偶数的二进制的末尾 为 0,奇数的二进制的末尾 为 1
十进制 m 转换 n 进制方法: m 一直除 n,每相除一次,m 就等于商,直到商为 0,然后余数反排 即可。
1 | 1的二进制:1/2 =0 余1 |
其他方面
(n >> m) & 1 == (n >> m) | 0 == (n >> m) ^ 0
n & ~(1 << m) : 将 n 的第 m 位数,重置为 0
n | (1 << m) : 将 n 的第 m 位数,重置为 1
n ^ (1 << m) : 将 n 的第 m 位数,取其相反
结束语
转载本站文章请注明作者和出处 tomotoes.com,请勿用于任何商业用途。