緒言
我注意到一些人好像對位操作符不太清楚,所以我決定寫篇簡單的指南,說明如何使用他們。
位簡介
bits,你會問他們是什麼呢?
其實,簡單說,我們在電腦上處理各種工作都是由許多1和0完成的。我們在電腦上儲存的所有資料都是用bits來表示的。一個byte是用8個bit表示的,一個WORD是用兩個BYTE表示的,或者16個bit。一個DWORD是用兩個WORD表示的,或者32個bit.
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 -----------------------------+
使用位操作符的美妙之處在於你可以把BYTE, WORD 或 DWORD 當成一個小型的數組或者結構。你可以使用位操作符檢查單獨的一個位的值,設定某一個或一組位的值。
十六進位和他們與bits的關係
使用bits時,很難只使用二元符號1或者0去表示每個數值。為瞭解決這個問題我們使用十六進位(基數是16)數。
十六進位使用四個二進位位來表示從0到15的數字,這些數字也是單個的十六進位阿拉伯數字所能表示的範圍。由四個二進位位或一個BYTE的一半組成的組被稱為一個元組。一個BYTE包含兩個元組,所以我們可以使用兩個十六進位阿拉伯數字來表示一個BYTE類型的值。
NIBBLE HEX VALUE
====== =========
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
因此,我們可以像下面這樣用一個BYTE來表示字母'r'(ASCII碼是114):
0111 0010 binary
7 2 hexadecimal
我們可以把它寫成'0x72'.
二進位操作符
有六個位操作符,他們是:
& 與
| 或
^ 異
~ 按位求反
>> 右移
<< 左移
& 與操作符
&操作符比較兩個數,只有要比較的兩個值的相應位都被設定(為1-譯者注)時,返回的值相應位才被設定。這些比較位使用下面的表進行比較:
1 & 1 == 1
1 & 0 == 0
0 & 1 == 0
0 & 0 == 0
這個操作符理想的應用是建立一個掩碼來檢查某個位的值。假設我們有一個包含某些位標誌的位元組,我們想檢查它的位4是否被設定(即是否被置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
因此我們知道位4被置1了。
| 操作符
| 操作符比較兩個數,只有他們相應位中的一個或兩個同時被設定時,傳回值相應位就會被設定。這些比較位使用下面的表進行比較:
1 | 1 == 1
1 | 0 == 1
0 | 1 == 1
0 | 0 == 0
這個操作符的理想應用是確保某個位被設定。假設我們想某個值的位3一定被設定:
BYTE b = 50;
BYTE c = b | 0x04;
cout << "c = " << c << endl;
這會發生如下的計算:
00110010 - b
| 00000100 - | 0x04
----------
00110110 - result
^操作符
^操作符比較兩個數,只有這兩個數的相應位標誌不同時,返回數的相應位才會被設定。這些比較位使用下面的表進行比較:
1 ^ 1 == 0
1 ^ 0 == 1
0 ^ 1 == 1
0 ^ 0 == 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,而不管這個資料的大小。假設除了位0和位1,我們想把其他的位置1:
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:
BYTE b = 50;
cout << "b = " << b << endl;
BYTE c = b & ~0x10;
cout << "c = " << c << endl;
這會發生如下的計算:
00110010 - b
& 11101111 - ~0x10
----------
00100010 - result
>>和<<操作符
>>(右移)和<<(左移)操作符按指定的位元移動位組。>>操作符將位組從高位向低位移。<<操作符將位組從低位向高位移。這兩個操作符的一個應用是由於某些原因(如,檢驗MKEWPARAM, HIWORD, 和 LOWORD宏)需要對齊位組。
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
位域
另一個可以使用位的有意思的事是使用位域。你可以使用位域在BYTE,WORD或DWORD內建立更小的結構。例如,假設我們想知道日期,但我們我想儘可能使用較少的記憶體。我們可以像下面這樣建立資料結構:
struct date_struct {
BYTE day : 5, // 1 to 31
month : 4, // 1 to 12
year : 14; // 0 to 9999
} date;
在這個例子中,'日'佔用了5個位,'月'佔用了接下來的4位,同時'年'佔用了接下來的14位。位24不用。如果我用整型定義每個域,這個結構將佔用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 --+
現在,注意它的聲明部分,看我們做了什麼。
首先,我們看我們對位域結構使用的資料類型。這裡我們用了BYTE。一個BYTE佔8位,編譯器將分配一個BYTE來存貯資料。如果在結構裡我們使用了超過了8位的空間,編譯器將分配另外的8位BYTE,直到能容納我們的結構為止。如果我們使用了WORD或DWORD,編譯器將分配一個總共32位的空間容納我們的結構。
現在,我們來看下不同的域是怎樣聲明的。首先,我們使用冒號分開網域名稱和位元。既然我們能獲得位域的地址,我們就能使用這個結構的地址。
date.day = 12;
dateptr = &date;
dateptr->year = 1852;