位段,C語言允許在一個(gè)結(jié)構(gòu)體中以位為單位來指定其成員所占內(nèi)存長(zhǎng)度,這種以位為單位的成員稱為“位段”或稱“位域”( bit field) 。利用位段能夠用較少的位數(shù)存儲(chǔ)數(shù)據(jù)。
定義信息的存取一般以字節(jié)為單位。實(shí)際上,有時(shí)存儲(chǔ)一個(gè)信息不必用一個(gè)或多個(gè)字節(jié),例如,“真”或“假”用0或1表示,只需1位即可。在計(jì)算機(jī)用于過程控制、參數(shù)檢測(cè)或數(shù)據(jù)通信領(lǐng)域時(shí),控制信息往往只占一個(gè)字節(jié)中的一個(gè)或幾個(gè)二進(jìn)制位,常常在一個(gè)字節(jié)中放幾個(gè)信息。
性質(zhì)位段(或稱“位域”,Bit field)為一種數(shù)據(jù)結(jié)構(gòu),可以把數(shù)據(jù)以位的形式緊湊的儲(chǔ)存,并允許程序員對(duì)此結(jié)構(gòu)的位進(jìn)行操作。這種數(shù)據(jù)結(jié)構(gòu)的好處:
可以使數(shù)據(jù)單元節(jié)省儲(chǔ)存空間,當(dāng)程序需要成千上萬個(gè)數(shù)據(jù)單元時(shí),這種方法就顯得尤為重要。
位段可以很方便的訪問一個(gè)整數(shù)值的部分內(nèi)容從而可以簡(jiǎn)化程序源代碼。
而位域這種數(shù)據(jù)結(jié)構(gòu)的缺點(diǎn)在于,其內(nèi)存分配與內(nèi)存對(duì)齊的實(shí)現(xiàn)方式依賴于具體的機(jī)器和系統(tǒng),在不同的平臺(tái)可能有不同的結(jié)果,這導(dǎo)致了位段在本質(zhì)上是不可移植的。
例子在C語言中,位段的聲明和結(jié)構(gòu)(struct)類似,但它的成員是一個(gè)或多個(gè)位的字段,這些不同長(zhǎng)度的字段實(shí)際儲(chǔ)存在一個(gè)或多個(gè)整型變量中。在聲明時(shí),位段成員必須是整形或枚舉類型(通常是無符號(hào)類型),且在成員名的后面是一個(gè)冒號(hào)和一個(gè)整數(shù),整數(shù)規(guī)定了成員所占用的位數(shù)。位域不能是靜態(tài)類型。不能使用&對(duì)位域做取地址運(yùn)算,因此不存在位域的指針,編譯器通常不支持位域的引用(reference)。以下程序則展示了一個(gè)位段的聲明:
struct CHAR{ unsigned int ch : 8; //8位 unsigned int font : 6; //6位 unsigned int size : 18; //18位};struct CHAR ch1;以下程序展示了一個(gè)結(jié)構(gòu)體的聲明:
struct CHAR2{ unsigned char ch; //8位 unsigned char font; //8位 unsigned int size; //32位};struct CHAR2 ch2;第一個(gè)聲明取自一段文本格式化程序,應(yīng)用了位段聲明。它可以處理256個(gè)不同的字符(8位),64種不同字體(6位),以及最多262,144個(gè)單位的長(zhǎng)度(18位)。這樣,在ch1這個(gè)字段對(duì)象中,一共才占據(jù)了32位的空間。而第二個(gè)程序利用結(jié)構(gòu)體進(jìn)行聲明,可以看出,處理相同的數(shù)據(jù),CHAR2類型占用了48位空間,如果考慮邊界對(duì)齊并把要求最嚴(yán)格的int類型最先聲明進(jìn)行優(yōu)化,那么CHAR2類型則要占據(jù)64位的空間。
無名位域如果位域的定義沒有給出標(biāo)識(shí)符名字,那么這是無名位域,無法被初始化。無名位域用于填充(padding)內(nèi)存布局。只有無名位域的比特?cái)?shù)可以為0。這種占0比特的無名位域,用于強(qiáng)迫下一個(gè)位域在內(nèi)存分配邊界對(duì)齊。
實(shí)現(xiàn)通常在大端序系統(tǒng)(如PowerPC),安排位域從最重要字節(jié)(most-significant byte)到最不重要位(least-significant byte),在一個(gè)字節(jié)內(nèi)部從最重要位(most-significant bit)到最不重要位(least-significant bit);而在小端序系統(tǒng)(如x86),安排位域從最不重要位(least-significant byte)到最重要字節(jié)(most-significant byte),在一個(gè)字節(jié)內(nèi)部從最不重要位(least-significant bit)到最重要位(most-significant bit)。共同遵從的原則是內(nèi)存字節(jié)地址從低到高,內(nèi)存內(nèi)部的比特編號(hào)從低到高。
Microsoft Visual C++實(shí)現(xiàn)
在一個(gè)整數(shù)(integer)內(nèi)的位域從最不重要位(least-significant)向最重要位(most-significant)依次分配。
相鄰的兩個(gè)位域如果基類型(underlying type)的長(zhǎng)度相同,在后的位域適合當(dāng)前內(nèi)存分配單元且沒有跨內(nèi)存分配邊界,那么這兩個(gè)位域分配到同一個(gè)(1、2或4字節(jié)的)分配單元。這可以通俗理解為:具有相同的基類型(underlying type)長(zhǎng)度的相鄰位域盡量裝入基類型的同一個(gè)對(duì)象,如果裝得下的話。
應(yīng)用1.位段的使用
(1)位段成員的類型必須指定為unsigned或int類型。
(2) 若某一位段要從另一個(gè)字開始存放,可用以下形式定義:
unsigned a:1;
unsigned b:2;一個(gè)存儲(chǔ)單元
unsigned:0;
unsigned c:3;另一存儲(chǔ)單元
a、b、c應(yīng)連續(xù)存放在一個(gè)存儲(chǔ)單元中,由于用了長(zhǎng)度為0的位段,其作用是使下一個(gè)位段從下一個(gè)存儲(chǔ)單元開始存放。因此,只將a、b存儲(chǔ)在一個(gè)存儲(chǔ)單元中,c另存在下一個(gè)單元(“存儲(chǔ)單元”可能是一個(gè)字節(jié),也可能是2個(gè)字節(jié),視不同的編譯系統(tǒng)而異)。
(3) 一個(gè)位段必須存儲(chǔ)在同一存儲(chǔ)單元中,不能跨兩個(gè)單元。如果第一個(gè)單元空間不能容納下一個(gè)位段,則該空間不用,而從下一個(gè)單元起存放該位段。
(4) 可以定義無名位段。
(5) 位段的長(zhǎng)度不能大于存儲(chǔ)單元的長(zhǎng)度,也不能定義位段數(shù)組。
(6) 位段可以用整型格式符輸出。
(7) 位段可以在數(shù)值表達(dá)式中引用,它會(huì)被系統(tǒng)自動(dòng)地轉(zhuǎn)換成整型數(shù)。
(8) 位段定義的第一個(gè)位段長(zhǎng)度不能為0。
2.STM32F10xxx系列中的應(yīng)用
bit_word_addr=bit_band_base+(byte_offset×32)+(bit_number×4)
其中:
bit_word_addr是別名存儲(chǔ)器區(qū)中字的地址,塌映射到某個(gè)目標(biāo)位。
bit_band_base是別名區(qū)的起始位。
byte_offset是包含目標(biāo)位的字節(jié)在位段里的序號(hào)。
bit_number是目標(biāo)位所在位置(0~31)。
例如:
0x22006008=0x22000000+(0x300×32)+(2×4)
對(duì)0x22006008地址的寫操作與對(duì)0x20000300字節(jié)的位2執(zhí)行讀—改—寫操作效果相似。1
本詞條內(nèi)容貢獻(xiàn)者為:
王沛 - 副教授、副研究員 - 中國(guó)科學(xué)院工程熱物理研究所