程序中的全部數在計算機內存中都是以二進制的形式儲存的。位運算直接對整數在內存中的二進制位進行操做。因爲位運算直接對內存數據進行操做,不須要轉成十進制,所以處理速度很是快。算法
(1),與(&)運算編程
"&"運算一般用於二進制取位操做,例如一個數 & 1 的結果就是取二進制的最末位。這能夠用來判斷一個整數的奇偶,二進制的最末位爲0表示該數爲偶數,最末位爲1表示該數爲奇數。windows
(2),或(|)運算數據結構
」|「運算一般用於二進制特定位上的無條件賦值,例如一個數 | 1 的結果就是把二進制最末位強行變成1。若是須要把二進制最末位變成0,對這個數| 1以後再減1就能夠了,其實際意義就是把這個數強行變成最接近的偶數。函數
(3),異或(^)運算加密
"^"運算一般用於對二進制的特定一位進行取反操做,由於異或能夠這樣定義: 0和1 異或0都不變,異或1則取反。對象
"^"運算的逆運算是它自己,也就是說兩次異或同一個數最後結果不變,即(a^b)^b = a。」^"運算能夠用於簡單的加密,好比你想對一個朋友說1314520,但怕別人知道,因而雙方約定拿生日19800205做爲密鑰,1314520 ^ 19800205 = 20590165, 因而就把20590165告訴這個朋友。你的朋友再次計算20590165^19800205的值,獲得1314520,因而它就明白了你的意圖。內存
加法和減法互爲逆運算,而且加法知足交換律。能夠寫出一個不須要臨時變量的swap函數it
void swap(int a, int b)io
{
a = a + b;
b = a - b;
a = a - b;
}
因爲^的逆運算是它自己,因而就有了一個很特別的swap過程:
void swap(int a, int b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
(4),取反(~)運算
「~」運算的定義是把內存中的0和1所有取反。使用「~」運算時要格外當心,須要注意整數類型有沒有符號。若是「~」的對象是無符號整數(不能表示負數),那麼獲得的值就是它與該類型上界的差。由於無符號類型的數是用00到$FFFF依次表示的。下面的程序返回65435。
#include <stdio.h>
int main()
{
unsigned short a = 100;
a = ~a;
printf("%d\n",a);
return 0;
}
若是「~」的對象是有符號的整數,狀況就不同了, 稍後會提到。
(5),左移(<<)運算
a << b 表示把 a 轉爲二進制後左移 b 位(在後面添b個0)。如100的二進制爲1100100,而110010000轉成十進制是400.能夠看出 a<< b的值實際上就是a 乘以 2的 b次方,由於在二進制數後面添加一個0 就至關於該數乘以2.
一般認爲 a <<1 比 a * 2更快,由於前者是更底層的操做,所以程序中乘以2的操做請儘可能用左移一位來代替。
定義一些常量可能會用到 <<運算。能夠方便地用(1 << 16) -1來表示65535.不少算法和數據結構要求數據規模必須是2的冪,此時能夠用<<來定義Max_N等常量。
(6),右移(>>)運算
和<<類似,a >> b 表示二進制右移b位(去掉末b位),至關於a除以2的b次方(取整)。此外也常常用 >> 1來代替除以2,好比二分查找, 堆的插入操做等。想辦法用>>代替除法運算,可使程序效率大大提升。最大公約數的二進制算法用除以2操做來代替mod運算,效率能夠提升60%。
常見的二進制位的變換操做
功能 | 示例 | 位運算 |
去掉最後一位 | (101101--->10110) | x >> 1 |
在最後加一個0 | (101101--->1011010) | x << 1 |
在最後加一個1 | (101101--->1011011) | (x << 1) + 1 |
把最後一位變成1 | (101100--->101101) | x | 1 |
把最後一位變成0 | (101101--->101100) | (x|1)-1 |
最後一位取反 | (101101--->101100) | x ^ 1 |
把右數第k位變成1 | (101001--->101101, k = 3) | x | (1 << (k -1)) |
把右數第k位變成0 | (101101--->101001,k = 3) | x & ~(1 <<(k -1)) |
右數第K位取反 | (101001--->101101,K= 3) | x ^(1 << (k - 1)) |
取末位三位 | (1101101--->101) | x & 7 |
取末K位 | (1101101--->1101,k = 5) | x & ((1 << k ) -1) |
取右數第 k位 | (1101101--->1, k = 4) | x >>(k -1) &1 |
把末K位變成1 | (101001--->101111, k = 4) | x | ((1 << k ) -1) |
末K位取反 | (101001--->100110, k = 4) | x ^ ((1<<k ) - 1) |
去掉整數最右邊的1 | (100101111--->10010110) | x & ( x - 1) |
把右邊連續的1變成0 | (100101111--->100100000) | x & ( x + 1) |
把右起第一個0 變成1 | (100101111--->100111111) | x | (x + 1) |
把右邊連續的0變成1 | (11011000--->11011111) | x | (x - 1) |
去掉右起第一個1 的左邊 | (100101000--->1000) | x & (x ^(x -1)) |
取右邊連續的1 | (100101111--->1111) | (x ^(x+1)) >> 1 |
在實際的編程過程當中,每每會用一個整數的不一樣位表示不一樣的數據信息。在訪問該整數時,就須要經過位運算來得到或者改變整數的某幾位數值。好比在windows中建立時使用的create數據結構:
struct{
PIO_SECURITY_CONTEXT SecurityCOntext;
ULONG Options;
USHORT POINTER_ALIGNMENT FileAttributes;
USHORT ShareAccess;
ULONG POINTER_ALIGNMENT Ealength;
PVOID EaBuffer;
LARGE_INTEGER AllocationSize;
}Create;
一般會引用其中的Options 以下:
Data->Iopb->Parameters.Create.Options
ULONG Options 是一個Windows文件建立過程當中的無符號長整數,指示在建立和打開文件時的不一樣選項。其中高8位指示了CreateDisposition參數(如FILE_OPEN, FILE_CREATE), 低24位指示了CreateOptions參數(如FILE_DELETE_ON_CLOSE)。
爲了獲得CreateDisposition的值,採起下面的位操做:
(Data->Iopb->Parameters.Create.Options >> 24) & 0x000000ff;
將該整數右移24位,再與0xff進行與操做, 便可得到CreateDisposition的值。
(2)將第n 位置位或清零。
#define BITN (1 << n)
置位: a |= BITN;
清零: a &= ~BITN;
(3)清除整數a最右邊的1
方法: a & (a -1)
問題: 如何判斷整數x的二進制中含有多少個1?
int func(x)
{
int countx = 0;
while(x)
{
countx ++;
x = x & (x -1);
}
return countx;
}
(4)將一個整數對齊到n:
a = (a + n -1) & ~(n -1)