本文屬於基礎類文章,只適合初學者,高手請止步。另外,本文的前置知識可以參考本站《 基於Delphi的Windows程序設計(一)》一文。
什麼是位(BIT)?其實就是字節的最小組成單位,例如:一個Byte類型的變量佔用1個字節,也就是佔用8位。一個Word類型佔用16位。熟悉C語言的朋友如果使用過位域,可能對此不陌生。例如,IP頭結構的定義:
1
2
3
4
5
6
|
/*
ip頭數據結構 */ struct
ip_header{ unsigned
char
h_len:4; /*
首部長度(4bytes單位),默認5 */ unsigned
char
version:4; /*
版本號 ipv4 */ unsigned
char
tos; /*
服務類型 */ ........ |
上面的“h_len:4”表示它只使用4個BIT,跟下面的“version:4”合起來,剛好是一個字(Byte)。爲什麼要這麼設置呢?因爲4個位已經足夠使用,能省一些是一些,你要知道,如果每個數據包都少幾個字節,總體傳輸起來是會減少非常多數據的。
再來看一個筆者幾年前寫的軟件,裏面有一個權限設置,就是標明某用戶擁有哪些權限:
比如說,功能有“屏幕控制、屏幕查看、文件管理、媒體播放、語音交流”,這些設置要保存到註冊表,這個時候,可以使用的方法是,每個功能對應一個Byte,如果值爲1,則表示允許;如果爲0,則表示禁止。如果使用這種方法,設置的時候,你要操作5次;判讀的時候,也要5次(因爲你有5個變量),如果使用位操作,那麼只需要一次即可。下面我們來說說如何幹。
首先,我們根據功能,定義幾個常量:
1
2
3
4
5
6
7
8
9
10
|
const MASK_ScreenControl
= $00000001 ; MASK_ScreenView
= $00000002 ; MASK_FileManager
= $00000004 ; MASK_MediaPlayer
= $00000008 ; MASK_VoiceChat
= $00000010 ; MASK_AccessAll
= MASK_ScreenControl or
MASK_ScreenView or MASK_FileManager
or
MASK_MediaPlayer or MASK_VoiceChat; |
然後定義個變量用於保存:
1
2
|
var byAccess: Byte ; |
設置權限的時候,如下操作:
1
2
3
4
5
|
byAccess:= 0 ; if
允許屏幕控制 then
byAccess:=byAccess or
MASK_ScreenControl; if
允許媒體播放 then
byAccess:=byAccess or
MASK_MediaPlayer; ...... //最後,將byAccess寫到註冊表。 |
判斷權限的時候,如下操作:
1
2
3
4
|
byAccess:=RegReadxxx //先從註冊表讀取設置 允許屏幕控制:=(byAccess
and
MASK_ScreenControl)<> 0 ; 允許媒體播放:=(byAccess
and
MASK_MediaPlayer)<> 0 ; ...... |
爲什麼上面的常量要這樣賦值呢?能不能改變呢?例如,把MASK_FileManager定義爲$00000003;可以麼?其實,這裏是有些講究的。讓我們把這些常量先轉換成二進:
1
2
3
4
5
6
|
const MASK_ScreenControl
= $00000001 ;=====> 00000001 MASK_ScreenView
= $00000002 ;=====> 00000010 MASK_FileManager
= $00000004 ;=====> 00000100 MASK_MediaPlayer
= $00000008 ;=====> 00001000 MASK_VoiceChat
= $00000010 ;=====> 00010000 |
先看賦值操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
byAccess:= 0 ; if
允許屏幕控制 then
byAccess:=byAccess or
MASK_ScreenControl; { byAccess
:00000000 MASK_ScreenControl:00000001 進行or操作後: byAccess
:00000001 } if
允許媒體播放 then
byAccess:=byAccess or
MASK_MediaPlayer; { byAccess
:00000001 MASK_MediaPlayer
:00001000 進行or操作後: byAccess
:00001001 } |
賦值操作好像跟常量定義無關,再來看取值操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
byAccess:=RegReadxxx //先從註冊表讀取設置,比如說爲9,就是00001001。 允許屏幕控制:=(byAccess
and
MASK_ScreenControl)<> 0 ; { byAccess
:00001001 MASK_ScreenControl:00000001 進行and操作後: byAccess
:00000001 結果不爲零,說明允許。 } 允許媒體播放:=(byAccess
and
MASK_MediaPlayer)<> 0 ; { byAccess
:00001001 MASK_MediaPlayer
:00001000 進行and操作後: byAccess
:00001000 結果不爲零,說明允許。 } ...... |
請讀者自行根據上面的流程試驗假如某個MASK設置爲$00000003的情況,可以發現判斷權限的時候就衝突了。
再來看一下前面C語言的位域,假設該結構只有兩個成員。
1
2
3
4
5
6
|
#pragma
pack(push,1); struct
ip_header{ unsigned
char
h_len:4; /*
首部長度(4bytes單位),默認5 */ unsigned
char
version:4; /*
版本號 ipv4 */ }
; #pragma
pack(pop); |
如果你打印出來,會發現sizeof(ip_header)等1---一個字節,也就是8個位。如果用Delphi來表達,應該怎麼寫呢?應該這樣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
type ip_header= packed
record h_len_version: Byte ; end ; var ip:ip_header; h_len,version: Byte ; begin //賦值: h_len:= 11 ; //4位,也就是最大值爲1111,也就是十進制的15,不能超過這個。 version:= 2 ; //同上 ip . h_len_version:=(h_len
shl
4 )
or
version; //取值: h_len:=h_len_version
shr
4 ; version:=h_len_version
and
15 ; end ; |
當然,在實際的應用中,你可以將一個字節分爲更多部分的位。比如說,RGB顏色分爲565和555兩種格式,565表示將一個Word(16位)按照5、6、5的順序存儲RGB三個顏色,實際上,你可以將其轉換成3、3、2,這樣一來,就只佔用一個字節,體積減少一半,而肉眼對這樣轉換後的圖像還是能接受的(上圖是565,下圖是332):