本篇由作者原创(部分参考)
注意:位操作只适用于整型数据的操作,对于float型和double型数据编译器会报错。
位操作有与、或、异或、取反、左移、右移六种,符号及运算规则如下:
符号 |
描述 |
运算规则 |
& |
与 |
两个位都为1时,结果才为1 |
| |
或 |
两个位都为0时,结果才为0 |
^ |
异或 |
两个位相同为0,相异为1 |
~ |
取反 |
0变1,1变0 |
<< |
左移 |
各二进位全部左移若干位,高位丢弃,低位补0 |
>> |
右移 |
各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
注意:1.位操作的优先级比较低,应该使用括号保证提前运算。
2.对于移位操作来讲必须要注意是算术移位还是逻辑移位(算术左移、右移主要进行有符号数的倍增、倍减。逻辑左移、 右移主要进行无符号数的倍增、倍减。)
逻辑移位:不考虑符号位,也就是不考虑正负数的移位(逻辑移位不管是左移还是右移都是空缺处补0)
算术移位:考虑符号位,也就是正负数的移位(算数移位要保证符号位不改变)
1)其中算术左移和逻辑逻辑左移的思想一样,缺位补0。
2)算术右移的移位过程中,缺位由符号位决定(符号位是1,缺位补1。符号位是0,缺位补0)
注:(数据在计算机以补码存储)补码在计算机算术左移过程中符号位可能发生改变(溢出)。
位操作可以对某字节中的某段字段赋值、修改、取值(需要借助移位)等操作
实例:(这里以对字节为单位进行位操作,获取字节中的10)
#define TEMP_NUMBER_INITINAL 0x00000000 //十六进制表示 0
#define TEMP_NUMBER 0x000000a0 //十六进制表示 160
1 void main(){ 2 int number=NULL; 3 number = TEMP_NUMBER; 4 cout<<(number>>8)<<endl;//此时输出的是10 5 }
位操作的一般应用:
1.判断奇偶
只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if (a & 1 == 0)代替if (a % 2 == 0)来判断a是不是偶数。
下面程序将输出0到100之间的所有奇数。
[cpp] view plaincopy
- for (i = 0; i < 100; ++i)
- if (i & 1)
- printf("%d ", i);
- putchar(‘\n‘);
2.交换两数
一般的写法是:
[cpp] view plaincopy
- void Swap(int &a, int &b)
- {
- if (a != b)
- {
- int c = a;
- a = b;
- b = c;
- }
- }
可以用位操作来实现交换两数而不用第三方变量:
[cpp] view plaincopy
- void Swap(int &a, int &b)
- {
- if (a != b)
- {
- a ^= b;
- b ^= a;
- a ^= b;
- }
- }
可以这样理解:
第一步 a^=b 即a=(a^b);
第二步 b^=a 即b=b^(a^b),由于^运算满足交换律,b^(a^b)=b^b^a。由于一个数和自己异或的结果为0并且任何数与0异或都会不变的,所以此时b被赋上了a的值。
第三步 a^=b 就是a=a^b,由于前面二步可知a=(a^b),b=a,所以a=a^b即a=(a^b)^a。故a会被赋上b的值。
再来个实例说明下以加深印象。int a = 13, b = 6;
a的二进制为 13=8+4+1=1101(二进制)
b的二进制为 6=4+2=110(二进制)
第一步 a^=b a = 1101 ^ 110 = 1011;
第二步 b^=a b = 110 ^ 1011 = 1101;即b=13
第三步 a^=b a = 1011 ^ 1101 = 110;即a=5
3.变换符号
变换符号就是正数变成负数,负数变成正数。
如对于-11和11,可以通过下面的变换方法将-11变成11
1111 0101(二进制) –取反-> 0000 1010(二进制) –加1-> 0000 1011(二进制)
同样可以这样的将11变成-11
0000 1011(二进制) –取反-> 0000 1010(二进制) –加1-> 1111 0101(二进制)
因此变换符号只需要取反后加1即可。完整代码如下:
[cpp] view plaincopy
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- #include <stdio.h>
- int SignReversal(int a)
- {
- return ~a + 1;
- }
- int main()
- {
- printf("对整数变换符号 --- by MoreWindows( http://blog.csdn.net/MoreWindows ) ---\n\n");
- int a = 7, b = -12345;
- printf("%d %d\n", SignReversal(a), SignReversal(b));
- return 0;
- }
4.求绝对值
位操作也可以用来求绝对值,对于负数可以通过对其取反后加1来得到正数。对-6可以这样:
1111 1010(二进制) –取反->0000 0101(二进制) -加1-> 0000 0110(二进制)
来得到6。
因此先移位来取符号位,int i = a >> 31;要注意如果a为正数,i等于0,为负数,i等于-1。然后对i进行判断——如果i等于0,直接返回。否之,返回~a+1。完整代码如下:
[cpp] view plaincopy
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- int my_abs(int a)
- {
- int i = a >> 31;
- return i == 0 ? a : (~a + 1);
- }
现在再分析下。对于任何数,与0异或都会保持不变,与-1即0xFFFFFFFF异或就相当于取反。因此,a与i异或后再减i(因为i为0或-1,所以减i即是要么加0要么加1)也可以得到绝对值。所以可以对上面代码优化下:
[cpp] view plaincopy
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- int my_abs(int a)
- {
- int i = a >> 31;
- return ((a ^ i) - i);
- }