一、C語言的六種位運算子:
& 按位與
| 按位或
^ 按位異或
~ 取反
< 左移
> 右移
1. 按位與運算
按位與運算子”&”是雙目運算子。
其功能是參與運算的兩數各對應的二進位相與。只有對應的兩個二進位均為1時,結果位才為1 ,否則為0。參與運算的數以補碼方式出現。
例如:9&5可寫算式如下: 00001001 (9的二進位制補碼)&00000101 (5的二進位制補碼) 00000001 (1的二進位制補碼)可見9&5=1。
按位與運算通常用來對某些位清0或保留某些位。例如把a 的高八位清 0 , 保留低八位, 可作 a&255 運算 ( 255 的二進位制數為0000000011111111)。
1 2 3 4 5 |
main(){ int a=9,b=5,c; c=a&b; printf("a=%d\nb=%d\nc=%d\n",a,b,c); } |
2.按位或運算
按位或運算子“|”是雙目運算子。
其功能是參與運算的兩數各對應的二進位相或。只要對應的二個二進位有一個為1時,結果位就為1。參與運算的兩個數均以補碼出現。
例如:9|5可寫算式如下: 00001001|00000101
00001101 (十進位制為13)可見9|5=13
1 2 3 4 |
main(){ int a=9,b=5,c; c=a|b; printf("a=%d\nb=%d\nc=%d\n",a,b,c); |
3.按位異或運算
按位異或運算子“^”是雙目運算子。
其功能是參與運算的兩數各對應的二進位相異或,當兩對應的二進位相異時,結果為1。參與運算數仍以補碼出現。
例如9^5可寫成算式如下: 00001001^00000101 00001100 (十進位制為12)
1 2 3 4 5 |
main(){ int a=9; a=a^15; printf("a=%d\n",a); } |
4. 求反運算
求反運算子~為單目運算子,具有右結合性。
其功能是對參與運算的數的各二進位按位求反。
例如~9的運算為: ~(0000000000001001)結果為:1111111111110110
5. 左移運算
左移運算子“<<”是雙目運算子。左移n位就是乘以2的n次方。
其功能把“<<”左邊的運算數的各二進位全部左移若干位,由“<<”右邊的數指定移動的位數,高位丟棄,低位補0。
1)例: a<<4 指把a的各二進位向左移動4位。如a=00000011(十進位制3),左移4位後為00110000(十進位制48)。
2)例:
int i = 1;
i = i << 2; //把i裡的值左移2位
也就是說,1的2進位制是000…0001(這裡1前面0的個數和int的位數有關,32位機器,gcc裡有31個0),左移2位之後變成 000…0100,也就是10進位制的4,所以說左移1位相當於乘以2,那麼左移n位就是乘以2的n次方了(有符號數不完全適用,因為左移有可能導致符號變化,下面解釋原因)
需要注意的一個問題是:int型別最左端的符號位和移位移出去的情況. 我們知道,int是有符號的整形數,最左端的1位是符號位,即0正1負,那麼移位的時候就會出現溢位,
例如:
int i = 0x40000000; //16進位制的40000000,為2進位制的01000000…0000
i = i << 1;
那麼,i在左移1位之後就會變成0x80000000,也就是2進位制的100000…0000,符號位被置1,其他位全是0,變成了int型別所能表示的最小值,32位的int這個值是-2147483648,溢位.如果再接著把i左移1位會出現什麼情況呢?在C語言中採用了丟棄最高位的處理方法,丟棄了1之後,i的值變成了0.
左移裡一個比較特殊的情況是當左移的位數超過該數值型別的最大位數時,編譯器會用左移的位數去模型別的最大位數,然後按餘數進行移位,如:
int i = 1, j = 0x80000000; //設int為32位
i = i << 33; // 33 % 32 = 1 左移1位,i變成2
j = j << 33; // 33 % 32 = 1 左移1位,j變成0,最高位被丟棄
在用gcc編譯這段程式的時候編譯器會給出一個warning,說左移位數>=型別長度.那麼實際上i,j移動的就是1位,也就是33%32後的餘數.在gcc下是這個規則,別的編譯器是不是都一樣現在還不清楚.
總之左移就是: 丟棄最高位,0補最低位
6. 右移運算
右移運算子“>>”是雙目運算子。右移n位就是除以2的n次方
其功能是把“>>”左邊的運算數的各二進位全部右移若干位,“>>”右邊的數指定移動的位數。
例如:設 a=15,a>>2 表示把000001111右移為00000011(十進位制3)。
應該說明的是,對於有符號數,在右移時,符號位將隨同移動。當為正數時, 最高位補0,而為負數時,符號位為1,最高位是補0或是補1 取決於編譯系統的規定。Turbo C和很多系統規定為補1。
右移對符號位的處理和左移不同:
對於有符號整數來說,比如int型別,右移會保持符號位不變,例如:
int i = 0x80000000;
i = i >> 1; //i的值不會變成0x40000000,而會變成0xc0000000
就是說,對於有符號數, 符號位向右移動後,正數的話補0,負數補1,
對於有符號數,在右移時,符號位將隨同移動:
當為正數時, 最高位補0,
而為負數時,符號位為1,
也就是組合語言中的算術右移.同樣當移動的位數超過型別的長度時,會取餘數,然後移動餘數個位.
最高位是補0或是補1 取決於編譯系統的規定。Turbo C和很多系統規定為補1。
負數10100110 >>5(假設字長為8位),則得到的是 11111101
總之,在C中,左移是邏輯/算術左移(兩者完全相同),右移是算術右移,會保持符號位不變.實際應用中可以根據情況用左/右移做快速的乘/除運算,這樣會比迴圈效率高很多. x>>1; //相當於 x /= 2x<<1; //相當於 x *= 2x>>2; // x /=4x<<2; // x *= 4x>>3; // x /= 8x<<3; // x *= 8以此類推.
無符號:
1 2 3 4 5 6 7 8 9 |
main(){ unsigned a,b; printf("input a number: "); scanf("%d",&a); b=a>>5; b=b&15; printf("a=%d\tb=%d\n",a,b); } |
請再看一例!
1 2 3 4 5 6 7 8 9 |
main(){ char a='a',b='b'; int p,c,d; p=a; p=(p<<8)|b; d=p&0xff; c=(p&0xff00)>>8; printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d); } |
二、異或操作的妙用
1. 使特定位翻轉 要使哪幾位翻轉就將與其進行∧運算的該幾位置為1即可。
2 與0相∧,保留原值.
3.交換兩個值,不用臨時變數.
我們可以在不用引入其他變數就可以實現變數值的交換
用異或操作可以實現:
a = a^b; //(1)
b = a^b; //(2)
a = a^b; //(3)
異或操作滿足結合律和交換律,且由異或操作的性質知道,對於任意一個整數a^a=0;
證:(第(2)步中的a) a = a^b =(將第(1)步中的b代入b) a^(a^b) = b;
(第(3)步中的b)b =a^b = (將第(1)步中的b代入b,將第(2)步中的a代入a) a^b^a^a^b = a^a^a^b^b = a;
三 、位與運算
1 . 清零 A數中為1的位,B中相應位為0。然後使二者進行&運算,即可達到對A清零目的。
2 . 取一個數中某些指定位 取數A的某些位,把數B的某些位置1,就把數A的某些位與1按位與即可。
3 . 保留一位的方法 數A與數B進行&運算,數B在數A要保留的位1,其餘位為零。
4 . 判斷奇偶性 將變數 a的奇偶性。a與1做位與運算,若結果是1,則 a是奇數;將 a與1做位與運算,若結果是0,則 a是偶數。
四、應用舉例
1 .判斷int型變數a是奇數還是偶數
1 2 |
a&1 = 0 偶數 a&1 = 1 奇數 |
2 . 取int型變數a的第k位
1 |
(k=0,1,2……sizeof(int)),即a>>k&1 |
3 . 將int型變數a的第k位清0,即a=a&~(1<<k)
4 . 將int型變數a的第k位置1, 即a=a|(1<<k)
5 . int型變數迴圈左移k次,即a=a<<k|a>>16-k (設sizeof(int)=16)
6 . int型變數a迴圈右移k次,即a=a>>k|a<<16-k (設sizeof(int)=16)
7. 整數的平均值
對於兩個整數x,y,如果用 (x+y)/2 求平均值,會產生溢位,因為 x+y 可能會大於INT_MAX,但是我們知道它們的平均值是肯定不會
1 2 3 4 |
int average(int x, int y) //返回X,Y 的平均值 { return (x&y)+((x^y)>>1); } |
8 . 判斷一個整數是不是2的冪,對於一個數 x >= 0,判斷他是不是2的冪
1 2 3 4 |
boolean power2(int x) { return ((x&(x-1))==0)&&(x!=0); } |
9 不用temp交換兩個整數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void swap(int x , int y) { x ^= y; y ^= x; x ^= y; } php: $a ='dd'; $b = 'bb'; $a = $a ^ $b; $b = $a ^ $b; $a = $a ^ $b; echo $a,' ', $b; |
10 計算絕對值
1 2 3 4 5 6 |
int abs( int x ) { int y ; y = x >> 31 ; return (x^y)-y ; //or: (x+y)^y } |
11. 取模運算轉化成位運算 (在不產生溢位的情況下)
a % (2^n) 等價於 a & (2^n – 1)
12 乘法運算轉化成位運算 (在不產生溢位的情況下)
a * (2^n) 等價於 a<< n
13. 除法運算轉化成位運算 (在不產生溢位的情況下)
a / (2^n) 等價於 a>> n
例: 12/8 == 12>>3
14 . a % 2 等價於 a & 1 ( a & log2(2))
a % 4 等價於 a & 2 ( a & log2(4))
…..
a % 32 等價於 a & 5
15 if (x == a) x= b;
else x= a;
等價於 x= a ^ b ^ x;
16 x 的 相反數 表示為 (~x+1)