定義
信息的存取一般以
位元組為單位。實際上,有時存儲一個信息不必用一個或多個位元組,例如,“真”或“假”用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.位段的使用
(2) 若某一位段要從另一個字開始存放,可用以下形式定義:
unsigned a:1;
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執行讀—改—寫操作效果相似。