天天看點

C/C++位操作簡介

前言

我注意到有些人對位運算感到困惑,是以我決定寫這篇簡單的教程來說明位運算如何操作。

位簡介

位,它是什麼?你可能會問。

簡單來說,位就是1和0,在電腦中做的每一件事都是由它們組成的。電腦中所有的資料使用的是位。一個位元組由8個位組成;一個字由兩個位元組組成,即16個位;而一個雙字由四個位元組組成,即32個位。

 0 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 1 1 1 0 1 0 0 0 1 1 1 1 0 0 0
||              |               |               |              ||
|+- bit 31      |               |               |       bit 0 -+|
|               |               |               |               |
+-- BYTE 3 -----+--- BYTE 2 ----+--- BYTE 1 ----+-- BYTE 0 -----+
|                               |                               |
+----------- WORD 1 ------------+----------- WORD 0 ------------+
|                                                               |
+--------------------------- DWORD -----------------------------+
      

使用位元組,字或者雙字來進行位操作顯得比較美觀,就像使用一個小型數組或結構。使用位運算,可以檢查或設定單獨某一位的值或組位的值。

十六進制數和位的關系

人們發現,使用二進制計數法表示數字比較的困難。為避免這一問題,采用十六進制計數法(基數為16)。

十六進制的一位數字從0到15分别用二進制的四位來表示。四位一組,即半位元組。一個位元組有兩個半位元組,則可以用兩位十六進制數表示一個位元組的值。

半位元組     十六進制數
======   =========
 0000        0
 0001        1
 0010        2
 0011        3
 0100        4
 0101        5
 0110        6
 0111        7
 1000        8
 1001        9
 1010        A
 1011        B
 1100        C
 1101        D
 1110        E
 1111        F      

如果有一個位元組,内容為字母‘r’(ASCII 碼 114),則表示如下:

0111 0010 二進制數
  7    2  十六進制數      
記為:0x72      

位運算符

共有6種位運算符,如下:
  &  與運算符
  |  或運算符
  ^  異或運算符
  ~  取反運算符
 >>  右移運算符
 <<  左移運算符      

&  運算符

&(與)運算要求有兩個運算值,然後傳回一個值,當且僅當兩個運算值都位1時,傳回值為1。如下表:

   1   &   1   ==   1

   1   &   0   ==   0

   0   &   1   ==   0

   0   &   0   ==   0

一個位元組可以包含位标志,而使用與運算可以通過設定掩碼來檢查某位的值。算法如下:它用來判斷位元組中的第四位是否為1

BYTE b = 50;
if ( b & 0x10 )
    cout << "Bit four is set" << endl;
else
    cout << "Bit four is clear" << endl;      
通過以下計算可以得到結果:
    00110010  - b
  & 00010000  - & 0x10
  ----------
    00010000  - result
是以,第四位為1。      

| 運算符

|(或)運算符要求兩個運算值,然後傳回一個值,當且僅當兩個運算值中有一個為1或都為1時,傳回值為1。如下表:

   1   |   1   ==   1

   1   |   0   ==   1

   0   |   1   ==   1

   0   |   0   ==   0

使用或運算可以保證位元組中的某位為1。算法如下:它用來保證第二位總是為1

BYTE b = 50;
BYTE c = b | 0x04;
cout << "c = " << c << endl;      
通過以下計算可以得到結果:
    00110010  - b
  | 00000100  - | 0x04
  ----------
    00110110  - result
    
    
^ 異或運算符
^ (異或)運算符要求有兩個運算值,然後傳回一個值,當且僅當兩個運算值中有一個為1但不同時為1時,傳回值為1。如下表:
   1   ^   1   ==   0
   1   ^   0   ==   1
   0   ^   1   ==   1
   0   ^   0   ==   0
   
使用異或運算可以翻轉特定的位。即0變1,1變0。算法如下:翻轉第三和第四位      
BYTE b = 50;
cout << "b = " << b << endl;
b = b ^ 0x18;
cout << "b = " << b << endl;
b = b ^ 0x18;
cout << "b = " << b << endl;      
通過以下計算可以得到結果:
    00110010  - b
  ^ 00011000  - ^ 0x18
  ----------
    00101010  - result
    
    00101010  - b
  ^ 00011000  - ^ 0x18
  ----------
    00110010  - result
    
    
~ 取反運算符
~(取反)運算符隻要求一個運算值,然後将所有的1變成0,所有的0變成1。使用取反運算可以将某些位元組置0,確定其它位元組置1,而不用考慮資料的大小。算法如下:将所有位置1,而第一和第零位置0      
BYTE b = ~0x03;
cout << "b = " << b << endl;
WORD w = ~0x03;
cout << "w = " << w << endl;      
通過以下計算可以得到結果:
    00000011  - 0x03
    11111100  - ~0x03  b
    0000000000000011  - 0x03
    1111111111111100  - ~0x03  w
    
同&(與)運算符一起使用,可以使任意位置0。算法如下:将第四位置0      
BYTE b = 50;
cout << "b = " << b << endl;
BYTE c = b & ~0x10;
cout << "c = " << c << endl;      
通過以下計算可以得到結果:
    00110010  - b
  & 11101111  - ~0x10
  ----------
    00100010  - result
    
    
>>和<< 右移和左移運算符
>>(右移)運算符和<<(左移)運算符将資料右移或左移若幹位。>>右移運算從高位往低位移,<<左移運算從低位往高位移。      
BYTE b = 12;
cout << "b = " << b << endl;
BYTE c = b << 2;
cout << "c = " << c << endl;
c = b >> 2;
cout << "c = " << c << endl;      
通過以下計算可以得到結果:
    00001100  - b
    00110000  - b << 2
    00000011  - b >> 2
    
    
位段
位段是位運算中比較令人感興趣的部分。使用位段可以在一個位元組,字或雙字内設定小型結構。例如:要記錄日期,要求盡可能少的使用記憶體,則可以采用如下的結構申明:      
struct date_struct {
    BYTE day   : 5,   // 1 to 31
         month : 4,   // 1 to 12
         year  : 14;  // 0 to 9999
    } date;
    
在上面的例子中,日占據了最低的5位,月份占據了接下來的4位,年份為接下來的14位。則整個日期儲存在三個位元組的23位中。第二十四位被忽略。如果使用整形申明則将占據12個位元組。      
|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
  |                           |       |         |
  +------ year ---------------+ month +-- day --+
  
如上所述,date類型使用的位段結構。這裡使用的是BYTE。一BYTE為8位,使用的時候,編譯器将申請一個BYTE來儲存。如果結構超過8位,編譯器将再申請一個BYTE,直到足夠用來儲存結構。如果使用字或雙字,編譯器将總共申請32位用來儲存結構。      

怎樣申明位段?首先申明位段變量,跟着冒号,然後是配置設定給變量的位數;每位段用逗号分隔,最後用分号表示申明結束。

完成結構申明後,則可以通過存取标記友善的使用結構,同時也可以使用位址操作符使用結構的位址對結構進行操作。如下:

date.day = 12;
dateptr = &date;
      

繼續閱讀