位運算(&, |, ^, >> , <<)


一. 位操作基礎,用一張表描述位操作符的應用規則並詳細解釋。

      二. 常用位操作小技巧,有判斷奇偶、交換兩數、變換符號、求絕對值。

      三. 位操作與空間壓縮,針對篩素數進行空間壓縮。

      四. 位操作的趣味應用,列舉了位操作在高低位交換、二進制逆序、二進制中1的個數以及缺失的數字這4種趣味應用。

希望讀者能認真學習和親自上機輸入代碼進行實驗,相信通過本文及適當的練習可以使你對位操作有更加深入的了解,在筆試面試中遇到位操作相關試題能更加從容。

一. 位操作基礎

基本的位操作符有與、或、異或、取反、左移、右移這6種,它們的運算規則如下所示:

 

符號

 描述

 運算規則                        by MoreWindows

&      

 與

兩個位都為1時,結果才為1

|  

 或    

兩個位都為0時,結果才為0

^    

異或

兩個位相同為0,相異為1

~   

取反

0變1,1變0

<< 

左移

各二進位全部左移若干位,高位丟棄,低位補0

>> 

右移

各二進位全部右移若干位,對無符號數,高位補0,有符號數,各編譯器處理方法不一樣,有的補符號位(算術右移),有的補0(邏輯右移)

 

 

 

1.判斷奇偶

只要根據最未位是0還是1來決定,為0就是偶數,為1就是奇數。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)來判斷a是不是偶數。

下面程序將輸出0到100之間的所有奇數。

[cpp]  view plain copy
 
  1. for (i = 0; i < 100; ++i)  
  2.     if (i & 1)  
  3.         printf("%d ", i);  
  4. putchar('\n');  

2.交換兩數

一般的寫法是:

[cpp]  view plain copy
 
  1. void Swap(int &a, int &b)  
  2. {  
  3.     if (a != b)  
  4.     {  
  5.         int c = a;  
  6.         a = b;  
  7.         b = c;  
  8.     }  
  9. }  

可以用位操作來實現交換兩數而不用第三方變量:

[cpp]  view plain copy
 
  1. void Swap(int &a, int &b)  
  2. {  
  3.     if (a != b)  
  4.     {  
  5.         a ^= b;  
  6.         b ^= a;  
  7.         a ^= b;  
  8.     }  
  9. }  

可以這樣理解:

第一步  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=6

3.變換符號

變換符號就是正數變成負數,負數變成正數。

如對於-11和11,可以通過下面的變換方法將-11變成11

      1111 0101(二進制) –取反-> 0000 1010(二進制) –加1-> 0000 1011(二進制)

同樣可以這樣的將11變成-11

      0000 1011(二進制) –取反-> 0000 0100(二進制) –加1-> 1111 0101(二進制)

因此變換符號只需要取反后加1即可。完整代碼如下:

[cpp]  view plain copy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )    
  2. #include <stdio.h>  
  3. int SignReversal(int a)  
  4. {  
  5.     return ~a + 1;  
  6. }  
  7. int main()  
  8. {  
  9.     printf("對整數變換符號 --- by MoreWindows( http://blog.csdn.net/MoreWindows )  ---\n\n");  
  10.     int a = 7, b = -12345;  
  11.     printf("%d  %d\n", SignReversal(a), SignReversal(b));  
  12.     return 0;  
  13. }  

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 plain copy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )  
  2. int my_abs(int a)  
  3. {  
  4.     int i = a >> 31;  
  5.     return i == 0 ? a : (~a + 1);  
  6. }  

現在再分析下。對於任何數,與0異或都會保持不變,與-1即0xFFFFFFFF異或就相當於取反。因此,a與i異或后再減i(因為i為0或-1,所以減i即是要么加0要么加1)也可以得到絕對值。所以可以對上面代碼優化下:

[cpp]  view plain copy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )  
  2. int my_abs(int a)  
  3. {  
  4.     int i = a >> 31;  
  5.     return ((a ^ i) - i);  
  6. }  

注意這種方法沒用任何判斷表達式,而且有些筆面試題就要求這樣做,因此建議讀者記住該方法(^_^講解過后應該是比較好記了)。

 

 

 

實際應用:

位運算存儲數據

  利用位運算存儲數據,主要是為了減少程序占用的內存。以int數據為例子,如果按照十進制的方式存儲數據,一個32位的int變量只能存儲一個數值,而如果使用二進制方式存儲數據(缺點是只能存儲0或1兩個數據)則可以存儲32個數據,將極大的節約內存。例如,在一個int變量的從右側開始倒數第2位存儲數據,則存儲和讀取數據的代碼如下所示:

int bData = 0;
//存儲數值1 
bData = bData |(1 << (2 - 1));
//存儲數值0 
bData = bData & (~(1 << (2 - 1));
//讀取數據 
int n = bData & (1 << (2 - 1));

  點評:在該代碼中,將需要存儲的數據(0或1)存儲在變量bData的倒數第二位中,所以在存儲時,則只需要將倒數第二位的數值改變,其他位的數值不改變即可。所以在存儲1時,不論bData的數值是多少,只需要和二進制數據10進行位或運算就可以將倒數第二位置1,而需要存儲0時,則需要bData和0xfffffffd進行位與運算,即可達到清零倒數第二位的目的。

  需要注意的是,有些可能會認為在存儲數值0時,會使用如下的代碼實現:

bData = bData |(0 << (2 - 1));

  其實這樣是錯誤的,因為0無論左移多少位都還是0,這樣在進行位或運算時,0和1位或得到的結果會是1,無法實現設置對應的位為0的目的,所以需要使用以上的代碼進行實現。

  異或位運算簡單加密

  利用位運算的計算速度快,以及異或的特性(和同一個數字異或兩次還是自身),可以用來簡單加密數據,且加解密的速度會非常快。這種加密方式強度比較低,但是可以用於一般的加解密任務。例如,假設密匙是數字123,則加解密一個byte數組的代碼如下:

byte[] b = {1,2,3,5,6};
byte key = 123;
//依次加密的代碼 
for(int i = 0;i < b.length;i++){
b[i] = (byte)(b[i] ^ key); //利用異或加密 
}
//解密的代碼 
for(int i = 0;i < b.length;i++){
b[i] = (byte)(b[i] ^ key); //利用異或解密
}

  點評:在該代碼中,對數組b使用密匙key進行加密,加密的過程是將數組b中每個元素和key進行異或,加密以后的數據可以在實際應用中進行存儲或網絡傳輸,而解密時的操作和加密時一樣,使用這種簡單的加密方式雖然保密性不高,但是加解密的速度確實是很值得稱贊的。

位運算

>> 右移 右移一位相當於除2,右移n位相當於除以2的n次方。

例: 8>>2   相當於  8/2 = 4  

<< 左移 左移一位都相當於乘以2的1次方,左移n位就相當於乘以2的n次方 

例: 3<<2    相當於  3*4 = 12

異或兩次還是原來本身

例: a = a^b^b


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM