位域之我見

有些信息在存儲時,並不需要佔用一個完整的字節, 而只需佔幾個或一個二進制位。例如在存放一個開關量時,只有0和1 兩種狀態,用一位二進位即可。爲了節省存儲空間,並使處理簡便,C語言提供了一種數據結構,稱爲“位域”或“位段”。所謂“位域”是把一個類型單元中的二進位劃分爲幾個不同的區域, 並說明每個區域的位數。每個域有一個域名,允許在程序中按域名進行操作。 這樣就可以達到壓縮數據的目的。

位域定義

位域定義與結構定義相仿,其形式爲:

struct slabel {

    位域類型 位域名:位域bit長度;

    位域類型 :位域bit長度;

    

};

在K&R中,規定的位域類型只能是ints——這應該是包括signed和unsigned的char、short和int。而且最好註明是signed還是unsigned類型的。誠然,C99中增加了新的支持類型,不過爲了兼容性,還是遵循以前的標準吧。據我所知,也就是Borland、GCC的最新版本才能比較完善地支持C99,而Microsoft似乎並不支持,而且看上去將來也不會打算支持了。順便發句牢騷,C99出來後C和C++似乎分道揚鑣了,不爽啊。希望C++的新標準能讓它們貼近點吧。

         實際上,我覺得位域可以看作是取值範圍縮小了的位域類型:所有對位域類型能做的操作,對位域都能做。如對int型的位域,可以用printf(“%d”,位域名)來輸出,也可以用scanf(“%d”,&位域名)來進行輸入。

位域的壓縮與對齊

         位域的內存對齊有其特殊的地方,因爲在對齊的過程中不僅穿插着數據的壓縮,而且對齊也分爲字段對齊和位域類型對齊兩種。而這裏的位域類型對齊實際上就是一般的數據內存對齊。具體的壓縮與對齊規則如下:

1)       壓縮:如果相鄰位域字段的位域類型相同,各個位域字段只佔定義時的bit長度。

struct test1 {

    char    a : 2;

    char    b : 3;

    char    c : 1;

};

其大小爲1 bytes,a和b和c在一個char中。

2)       壓縮和字段對齊:一個位域字段必須存儲在其位域類型的一個單元所佔空間中,不能橫跨兩個該位域類型的單元。也就是說,當某個位域字段正處於兩個該位域類型的單元中間時,只使用第二個單元,第一個單元剩餘的bit位置補(pad)0。

struct test2 {

    char    a : 2;

    char    b : 3;

    char    c : 7;

};

其大小爲2 bytes,a和b在一個char中,c在第二個char中。

3)       位域類型對齊:如果相鄰的位域字段的類型不同,在不同的位域類型間,按通用的對齊規則進行不同數據類型間的對齊(注意,struct的長度是其內部最寬類型的整數倍);同時在相同位域類型的字段間按以上兩條規則對齊。

struct test3

{

    char a:1;

    char :2;

    long b:3;

    char c:2;

};

其大小爲12bytes。

4)  終止壓縮:如果位域字段之間穿插着非位域字段,則不進行壓縮;

struct test3

{

    char a:1;

    char :2;

    long b;

    char c:2;

};

其大小爲12bytes,如果註釋掉long b,則爲1byte。

         








順便指出,沒有位域名的位域字段是不能使用的,只是用來佔位置。

問題和總結

1.       網上有一種說法是位域的長度不能跨字節,這是完全錯誤的。在壓縮與對齊規則的第二條中說明了正確的規定。其實K&R中的原話是“is a set of adjacent bits within a single implementation-defined storage unit that we will call a ``word.''“。首先,word不是字節的意思,而這個word是不是兩個字節,我看也不是。這裏的word指的應該是同一種位域類型在不同系統中不同的實現長度。正因爲如此,作者才用了implementation-defined這個詞。以下是證明可以跨字節的例程:

#include <stdio.h>

int  main()

{

    struct foo4 {

       char    a : 2;

       char    b : 3;

       int c : 18;

    };

    struct foo4 t;

    t.c = 50*1024;

    printf("%d",t.c);

    int len = sizeof(foo4);

    printf("%d",len);

    return 0;

}

輸出t.c是51200,可以看出c可以取到18bits。

2.       因爲在一個byte中,bit位也是有分big-endian和small-endian的,所以位域的移植性並不好。不過在嵌入式中由於有許多特定的設備,在此中可以盡情使用位域。

在本文中,位域類型就是數據類型,呵呵。位域可以看作是取值範圍縮小了的位域類型,所有對位域類型能做的操作,對位域都能做。比較難的地方在於位域字段的壓縮與對齊。嗯,還有signed和unsigned類型要特別注意。

發佈了5 篇原創文章 · 獲贊 0 · 訪問量 7853
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章