Delphi中位的應用

本文屬於基礎類文章,只適合初學者,高手請止步。另外,本文的前置知識可以參考本站《 基於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個位已經足夠使用,能省一些是一些,你要知道,如果每個數據包都少幾個字節,總體傳輸起來是會減少非常多數據的。
        再來看一個筆者幾年前寫的軟件,裏面有一個權限設置,就是標明某用戶擁有哪些權限:
bit
        比如說,功能有“屏幕控制、屏幕查看、文件管理、媒體播放、語音交流”,這些設置要保存到註冊表,這個時候,可以使用的方法是,每個功能對應一個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):

565

332

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章