定義
信息的存取一般以位元組為單位。實際上,有時存儲一個信息不必用一個或多個位元組,例如,“真”或“假”用0或1表示,只需1位即可。在計算機用於過程控制、參數檢測或數據通信領域時,控制信息往往只占一個位元組中的一個或幾個二進制位,常常在一個位元組中放幾個信息。
性質
位段(或稱“位域”,Bit field)為一種數據結構,可以把數據以位的形式緊湊的儲存,並允許程式設計師對此結構的位進行操作。這種數據結構的好處:
•可以使數據單元節省儲存空間,當程式需要成千上萬個數據單元時,這種方法就顯得尤為重要。
•位段可以很方便的訪問一個整數值的部分內容從而可以簡化程式原始碼。
而位域這種數據結構的缺點在於,其記憶體分配與記憶體對齊的實現方式依賴於具體的機器和系統,在不同的平台可能有不同的結果,這導致了位段在本質上是不可移植的。
例子
在C語言中,位段的聲明和結構(struct)類似,但它的成員是一個或多個位的欄位,這些不同長度的欄位實際儲存在一個或多個整型變數中。在聲明時,位段成員必須是整形或枚舉類型(通常是無符號類型),且在成員名的後面是一個冒號和一個整數,整數規定了成員所占用的位數。位域不能是靜態類型。不能使用&對位域做取地址運算,因此不存在位域的指針,編譯器通常不支持位域的引用(reference)。以下程式則展示了一個位段的聲明:
以下程式展示了一個結構體的聲明:
第一個聲明取自一段文本格式化程式,套用了位段聲明。它可以處理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執行讀—改—寫操作效果相似。