位域的定義
位域的定義格式如下:
struct 位域結(jié)構(gòu)體名
{
數(shù)據(jù)類型 位域名 :位域長(zhǎng)度;
};
標(biāo)準(zhǔn)數(shù)據(jù)類型
標(biāo)準(zhǔn)C規(guī)定位域的數(shù)據(jù)類型只有如下四種:
- unsigned int
- signed int
- int
- _Bool
例如;
/*無符號(hào)整型位域,范圍:0~7*/
unsigned int b:3;
/*有符號(hào)整型位域,范圍:-4~3*/
signed int b:3;
/*int 默認(rèn)是 signed int,范圍:0~7 或 -4~3*/
int b:3;
/*布爾類型位域,范圍:0~1*/
bool b:1;
注意:
在 ANSI C 中,位域數(shù)據(jù)類型只有 unsigned int ,signed int,int 三種,到 C99 才加入 _Bool 數(shù)據(jù)類型支持。
int 用作位域數(shù)據(jù)類型,表示無符號(hào)數(shù)時(shí),范圍為:07;表示有符號(hào)數(shù)時(shí),范圍為:-43 。
其他數(shù)據(jù)類型
細(xì)心的小伙伴可能已經(jīng)發(fā)現(xiàn)了,在【基礎(chǔ)篇】的示例代碼里,我用了 unsigned char 類型來定義位域成員,上面不是說標(biāo)準(zhǔn)C規(guī)定了只有 unsigned int、signed int、int 和 _Bool 這四種數(shù)據(jù)類型才能作為位域的數(shù)據(jù)類型嗎?為什么用 unsigned char 類型定義位域成員也行呢?
其實(shí)這個(gè)問題與編譯器有關(guān),單片機(jī)應(yīng)用程序開發(fā),用到的主流 IDE 一般都是 Keil 和 IAR ,這兩個(gè) IDE 都集成了編譯器,這些編譯器大多都在標(biāo)準(zhǔn)C上做了擴(kuò)展,增加了 char、signed char、unsigned char 等數(shù)據(jù)類型,所以使用 unsigned char 類型定義位域成員也是沒有問題的。
為了方便講解,下面的例子中我都會(huì)使用 unsigned char 類型定義位域成員。
位域名
位域名是可選項(xiàng),定義位域成員時(shí),是可以不寫位域名的,格式如下:
struct 位域結(jié)構(gòu)體名
{
數(shù)據(jù)類型 :位域長(zhǎng)度;
};
這些沒有位域名的位域,叫做無名位域,有些資料也叫匿名位域;無名位域沒有位域名,在程序中是無法引用無名位域的,無名位域一般用來填充指定位數(shù)或調(diào)整成員位置。
位域長(zhǎng)度
位域長(zhǎng)度就是指某個(gè)位域成員變量所占用的二進(jìn)制位數(shù)(bit),位域長(zhǎng)度不能超過定義該位域的數(shù)據(jù)類型的長(zhǎng)度。
這個(gè)很好理解,假設(shè)我定義了一個(gè)位域成員 a ,如下:
struct A
{
unsigned char a :8;
};
位域成員 a 是由 unsigned char 類型定義的,假設(shè) unsigned char 類型占用一字節(jié)空間,那么位域成員 a 的位域長(zhǎng)度就不能超過 8 。
位域的使用
位域的使用方法和結(jié)構(gòu)體一樣,有如下兩種形式:
位域變量名.位域名
位域變量名- >位域名
例如:
struct Bit_Field_t
{
unsigned char a :4;
unsigned char b :3;
};
struct Bit_Field_t Bit_Field;
struct Bit_Field_t *Bit_Field_p;
Bit_Field.a
Bit_Field_p- >a
需要注意的是,位域一般只占用一個(gè)字節(jié)的若干個(gè) bit(例如上述的位域 a只占用了一個(gè)字節(jié)的 4bit 空間),而且不一定位于字節(jié)的起始位置。另外,地址的操作單位是字節(jié),而不是 bit ,因此對(duì)獲取位域的地址是沒有意義的,例如下面的操作是不被允許的:
&Bit_Field.a
C11 中還規(guī)定,不能對(duì)位域使用 sizeof 和 _Alignas 這兩個(gè)關(guān)鍵字。
位域的存儲(chǔ)
測(cè)試環(huán)境
先說明一下我的測(cè)試環(huán)境,下面涉及到的代碼,是在ubuntu 系統(tǒng)下,使用 GCC 編譯的。
測(cè)試各常用變量長(zhǎng)度如下:
printf("sizeof(char) = %dn", sizeof(char));
printf("sizeof(short) = %dn", sizeof(short));
printf("sizeof(int) = %dn", sizeof(int));
printf("sizeof(long) = %dn", sizeof(long));
printf("n");
printf("sizeof(unsigned char) = %dn", sizeof(unsigned char));
printf("sizeof(unsigned short) = %dn", sizeof(unsigned short));
printf("sizeof(unsigned int) = %dn", sizeof(unsigned int));
printf("sizeof(unsigned long) = %dn", sizeof(unsigned long));
相鄰位域數(shù)據(jù)類型相同
當(dāng)相鄰的位域數(shù)據(jù)類型相同時(shí),各個(gè)位域都是緊挨著存儲(chǔ)的,如果剩余空間不足以存放下一個(gè)位域,則下一個(gè)位域從新的存儲(chǔ)單元開始存儲(chǔ),例如:
struct Bit_Field_t
{
unsigned char a :4;
unsigned char b :3;
unsigned char c :5;
};
struct Bit_Field_t Bit_Field;
位域 a 和位域 b 占用了 7bit 空間,剩余 1bit 空間不足以存儲(chǔ)位域 c ,所以位域 c 從下一存儲(chǔ)空間開始存儲(chǔ),unsigned char 類型占用一字節(jié)空間,所以位域 c 從下一字節(jié)開始存儲(chǔ),如下:
相鄰位域數(shù)據(jù)類型不同
當(dāng)相鄰的位域數(shù)據(jù)類型不同時(shí),不同數(shù)據(jù)類型的位域可能緊挨著存儲(chǔ),也有可能從下一個(gè)存儲(chǔ)單元開始存儲(chǔ),這個(gè)存儲(chǔ)機(jī)制與編譯器有關(guān),一般來說,GCC 編譯器會(huì)將不同數(shù)據(jù)類型的相鄰位域緊挨著存儲(chǔ),例如:
struct Bit_Field_t
{
unsigned char a :4;
unsigned char b :3;
unsigned short c :5;
};
struct Bit_Field_t Bit_Field;
位域 a 和位域 b 類型相同,沒有超出字節(jié)長(zhǎng)度,緊挨著存儲(chǔ),剩余 1bit 空間,位域 c 與位域 b 類型不同,如果編譯器將這兩個(gè)相鄰位域緊挨著存儲(chǔ)的話,示意圖如下:
位域之間有無名位域
當(dāng)兩個(gè)位域之間有無名位域時(shí),位域的存儲(chǔ)位置與無名位域長(zhǎng)度有關(guān)。
當(dāng)無名位域長(zhǎng)度為 0 時(shí),指定下個(gè)位域從下一個(gè)存儲(chǔ)單元開始存放,例如:
struct Bit_Field_t
{
unsigned char a : 3;
unsigned char : 0;
unsigned char b : 4;
};
struct Bit_Field_t Bit_Field;
unsigned char 類型占用一字節(jié)空間,位域 a 占用了 3bit 空間,緊挨著就是一個(gè)長(zhǎng)度為0的無名位域,這就意味著位域 b 需要從下一個(gè)存儲(chǔ)單元開始存儲(chǔ),unsigned char 類型占用一字節(jié)空間,所以位域 b 從下一字節(jié)開始存儲(chǔ)。
當(dāng)無名位域長(zhǎng)度大于 0 時(shí),跳過指定 bit 后再開始存放下一位域,例如:
struct Bit_Field_t
{
unsigned char a : 3;
unsigned char : 4;
unsigned char b : 4;
};
struct Bit_Field_t Bit_Field;
unsigned char 類型占用一字節(jié)空間,位域 a 占用了 3bit 空間,緊挨著就是一個(gè)長(zhǎng)度為4的無名位域,此時(shí)會(huì)跳過 4bit 不用,在 bit7 的位置開始存放位域 b 。
小結(jié)
位域的優(yōu)點(diǎn)很明顯,合理運(yùn)用的話可以極大地節(jié)省內(nèi)存資源;然而,位域的缺點(diǎn)也同樣明顯,不同的平臺(tái),不同的編譯器,不同的大小端模式,都會(huì)影響位域在內(nèi)存中的存儲(chǔ),因此,使用位域的程序,想在不同的平臺(tái)間移植,是件很頭疼的事情。
上面舉的例子,都是以單個(gè)字節(jié)作為存儲(chǔ)單元,以字節(jié)對(duì)齊的方式為前提講解的,在使用其他長(zhǎng)度的類型作為存儲(chǔ)單元以及使用其他對(duì)齊方式都會(huì)有不同的結(jié)果,這個(gè)大家在實(shí)際開發(fā)中需要靈活應(yīng)用。
評(píng)論
查看更多