一、什么是位运算
所谓位运算是指进行二进制位的运算
在系统软件中,常要处理二进位的问题
例如,将一个存储单元中的各二进位左移或右移一位,两个数按位相加等
二、位运算符和位运算
1、按位与 运算符(&)
参加运算的两个数据,按二进位进行“与”运算。如果两个相应的二进位都为1,则该位的结果值为1,否则为0
即0&0=0;0&1=0;1&0=0;&1=1;
例如: 3&5 并不等于8,应该是按位与。
如果参加&运算的是负数(如-3 & -5),则以补码形式表示为二进制数,然后按位进行“与”运算
按位与有一些特殊的用途:
- 清零
- 取一个数中某些指定位
实例:
#include <stdio.h>int main(int argc, char *argv[])
{int num1 = 3;int num2 = 5;int res = num1 & num2;printf("res = %d\n", res);return 0;
}
[root@localhost 10_bits]# gcc test1.c -o test1
[root@localhost 10_bits]# ./test1
res = 1
负数在内存中是使用的补码表示的
负数的补码表示规则:最高位表示符号(0表示正数,1表示负数),数据值采用反码加1来表示
一个负数在内存中的表示:先看符号位,如果符号位负,最高位就为1,对于数据值,将负数的绝对值取反用二进制表示,在此基础上加1,就是负数的补码表示
-1在内存中存储的时候存储的是1111…11(32个1)
#include <stdio.h>int main(int argc, char *argv[])
{int num1 = -1;int num2 = -3;int res = num1 & num2;printf("res = %d\n", res);return 0;
}
[root@localhost 10_bits]# gcc test1.c -o test1
[root@localhost 10_bits]# ./test1
res = -3
2、按位或运算符(|)
两个相应的二进位中只要有一个为1,该位的结果值为1
0|0=0; 0|1=1; 1|0=1; 1|1=1。
例如:060|017
将八进制数60与八进制数17进行按位或运算。
实例:
#include <stdio.h>int main(int argc, char *argv[])
{int num1 = 3;int num2 = 5;int res = num1 | num2; //011 | 101 == 111 == 7printf("res = %d\n", res);printf("num1 = %x\n", num1);return 0;
}
[root@localhost 10_bits]# gcc test1.c -o test1
[root@localhost 10_bits]# ./test1
res = 7
num1 = 3
3、“异或”运算符(∧)
异或运算符∧也称XOR运算符。它的规则是若参加运算的两个二进位同号,则结果为0(假);异号则为1(真)
0∧0=0; 0∧1=1; 1∧0=1; 1∧1=0;
如:
下面举例说明∧运算符的应用:
-
使特定位翻转
假设有01111010,想使其低4位翻转,即1变为0,0变为1。可以将它与00001111进行∧运算,即
-
与0相∧,保留原值
如: 012∧00=012
因为原数中的1与0进行∧运算得1,0∧0得0,故保留原数。 -
交换两个值,不用临时变量
假如a=3,b=4。想将a和b的值互换,可以用以下赋值语句实现:
a=a∧b;
b=b∧a;
a=a∧b;
4、“取反”运算符(~)
~是一个单目(元)运算符,用来对一个二进制数按位取反,即将0变1,1变0。
例如~025是对八进制数25(即二进制数00010101)按位求反。
5、左移运算符(<<)
用来将一个数的各二进位全部左移若干位。例如
a=a<<2
将a的二进制数左移2位,右补0。
若a=15,即二进制数00001111,左移2位得00111100,即十进制数60。
高位左移后溢出,舍弃不起作用。
左移1位相当于该数乘以2,左移2位相当于该数乘以22=4。。
但此结论只适用于该数左移时被溢出舍弃的高位中不包含1的情况。
左移比乘法运算快得多,有些C编译程序自动将乘2的运算用左移一位来实现,将乘2n的幂运算处理为左移n位
实例:
#include <stdio.h>int main(int argc, char *argv[])
{int num1 = 3;int num2 = 5;int res = num1 | num2; //011 | 101 == 111 == 7res = num1 << 3;printf("res = %d\n", res);printf("num1 = %x\n", num1);res = num2 >> 2; //101 >>2 printf("res = %d\n", res);num2 = -23; //10111-->11...01001res = num2 >> 3; //11...01001>>3 --> 111...01 --> -3printf("res = %d\n", res);num2 =0x83fffff3;res = num2 << 3;printf("res = %d\n", res);return 0;
}
[root@localhost 10_bits]# gcc test1.c -o test1
[root@localhost 10_bits]# ./test1
res = 24
num1 = 3
res = 1
res = -3
res = 536870808 //说明左移的时候符号位是会丢弃的
6、右移运算符(>>)
a>>2表示将a的各二进位右移2位
移到右端的低位被舍弃,对无符号数,高位补0
右移一位相当于除以2,右移n位相当于除以2n。
在右移时,需要注意符号位问题。
对无符号数,右移时左边高位移入0。
对于有符号的值,如果原来符号位为0(该数为正),则左边也是移入0
如果符号位原来为1(即负数),则左边移入0还是1,要取决于所用的计算机系统。有的系统移入0,有的移入1。移入0的称为“逻辑右移”,即简单右移。移入1的称为“算术右移”
实例:
#include <stdio.h>int main(int argc, char *argv[])
{int num1 = 3;int num2 = 5;int res = num1 | num2; //011 | 101 == 111 == 7res = num1 << 3;printf("res = %d\n", res);printf("num1 = %x\n", num1);res = num2 >> 2; //101 >>2 printf("res = %d\n", res);num2 = -23; //10111-->11...01001res = num2 >> 3; //11...01001>>3 --> 111...01 --> -3printf("res = %d\n", res);return 0;
}
[root@localhost 10_bits]# gcc test1.c -o test1
[root@localhost 10_bits]# ./test1
res = 24
num1 = 3
res = 1
res = -3
7、位运算赋值运算符
位运算符与赋值运算符可以组成复合赋值运算符如:&=, |=, >>=, <<=, ∧=
例如,a & = b相当于 a = a & b
a << =2相当于:a = a << 2
8、不同长度的数据进行位运算
如果两个数据长度不同(例如long型和int型)进行位运算时(如a & b,而a为long型,b为int型),系统会将二者按右端对齐
如果b为正数,则左侧16位补满0
若b为负数,左端应补满1
如果b为无符号整数型,则左侧添满0