複製鏈接
請複製以下鏈接發送給好友

位段

鎖定
位段,C語言允許在一個結構體中以位為單位來指定其成員所佔內存長度,這種以位為單位的成員稱為“位段”或稱“位域”( bit field) 。利用位段能夠用較少的位數存儲數據。
中文名
位段
別    名
位域
類    型
C語言
性    質
內存長度
功    能
能夠減少儲存數據的位數
定    義
信息的存取一般以字節為單位

位段定義

信息的存取一般以字節為單位。實際上,有時存儲一個信息不必用一個或多個字節,例如,“真”或“假”用0或1表示,只需1位即可。在計算機用於過程控制、參數檢測或數據通信領域時,控制信息往往只佔一個字節中的一個或幾個二進制位,常常在一個字節中放幾個信息。

位段性質

位段(或稱“位域”,Bit field)為一種數據結構,可以把數據以的形式緊湊的儲存,並允許程序員對此結構的位進行操作。這種數據結構的好處:
  • 可以使數據單元節省儲存空間,當程序需要成千上萬個數據單元時,這種方法就顯得尤為重要。
  • 位段可以很方便的訪問一個整數值的部分內容從而可以簡化程序源代碼。
而位域這種數據結構的缺點在於,其內存分配與內存對齊的實現方式依賴於具體的機器和系統,在不同的平台可能有不同的結果,這導致了位段在本質上是不可移植的。

位段例子

在C語言中,位段的聲明和結構(struct)類似,但它的成員是一個或多個位的字段,這些不同長度的字段實際儲存在一個或多個整型變量中。在聲明時,位段成員必須是整形或枚舉類型(通常是無符號類型),且在成員名的後面是一個冒號和一個整數,整數規定了成員所佔用的位數。位域不能是靜態類型。不能使用&對位域做取地址運算,因此不存在位域的指針,編譯器通常不支持位域的引用(reference)。以下程序則展示了一個位段的聲明:
struct CHAR
{
    unsigned int ch   : 8;    //8位
    unsigned int font : 6;    //6位
    unsigned int size : 18;   //18位
};
struct CHAR ch1;
以下程序展示了一個結構體的聲明:
struct CHAR2
{
    unsigned char ch;    //8位
    unsigned char font;  //8位
    unsigned int  size;  //32位
};
struct CHAR2 ch2;
第一個聲明取自一段文本格式化程序,應用了位段聲明。它可以處理256個不同的字符(8位),64種不同字體(6位),以及最多262,144個單位的長度(18位)。這樣,在ch1這個字段對象中,一共才佔據了32位的空間。而第二個程序利用結構體進行聲明,可以看出,處理相同的數據,CHAR2類型佔用了48位空間,如果考慮邊界對齊並把要求最嚴格的int類型最先聲明進行優化,那麼CHAR2類型則要佔據64位的空間。

位段無名位域

如果位域的定義沒有給出標識符名字,那麼這是無名位域,無法被初始化。無名位域用於填充(padding)內存佈局。只有無名位域的比特數可以為0。這種佔0比特的無名位域,用於強迫下一個位域在內存分配邊界對齊。

位段實現

通常在大端序系統(如PowerPC),安排位域從最重要字節(most-significant byte)到最不重要位(least-significant byte),在一個字節內部從最重要位(most-significant bit)到最不重要位(least-significant bit);而在小端序系統(如x86),安排位域從最不重要字節(least-significant byte)到最重要字節(most-significant byte),在一個字節內部從最不重要位(least-significant bit)到最重要位(most-significant bit)。共同遵從的原則是內存字節地址從低到高,內存內部的比特編號從低到高。
Microsoft Visual C++實現
在一個整數(integer)內的位域從最不重要位(least-significant)向最重要位(most-significant)依次分配。
相鄰的兩個位域如果基類型(underlying type)的長度相同,在後的位域適合當前內存分配單元且沒有跨內存分配邊界,那麼這兩個位域分配到同一個(1、2或4字節的)分配單元。這可以通俗理解為:具有相同的基類型(underlying type)長度的相鄰位域儘量裝入基類型的同一個對象,如果裝得下的話。

位段應用

1.位段的使用
(1)位段成員的類型必須指定為unsigned或int類型。
(2) 若某一位段要從另一個字開始存放,可用以下形式定義:
unsigned a:1;
unsigned b:2;一個存儲單元
unsigned:0;
unsigned c:3;另一存儲單元
a、b、c應連續存放在一個存儲單元中,由於用了長度為0的位段,其作用是使下一個位段從下一個存儲單元開始存放。因此,只將a、b存儲在一個存儲單元中,c另存在下一個單元(“存儲單元”可能是一個字節,也可能是2個字節,視不同的編譯系統而異)。
(3) 一個位段必須存儲在同一存儲單元中,不能跨兩個單元。如果第一個單元空間不能容納下一個位段,則該空間不用,而從下一個單元起存放該位段。
(4) 可以定義無名位段。
(5) 位段的長度不能大於存儲單元的長度,也不能定義位段數組
(6) 位段可以用整型格式符輸出。
(7) 位段可以在數值表達式中引用,它會被系統自動地轉換成整型數。
(8) 位段定義的第一個位段長度不能為0。
2.STM32F10xxx系列中的應用
bit_word_addr=bit_band_base+(byte_offset×32)+(bit_number×4)
其中:
bit_word_addr是別名存儲器區中字的地址,它映射到某個目標位。
bit_band_base是別名區的起始位。
byte_offset是包含目標位的字節在位段裏的序號。
bit_number是目標位所在位置(0~31)。
例如:
0x22006008=0x22000000+(0x300×32)+(2×4)
對0x22006008地址的寫操作與對0x20000300字節的位2執行讀—改—寫操作效果相似。 [1] 
參考資料
  • 1.    意法半導體.STM32F10xxx參考手冊:電子文檔,2009年6月:29