在c語言中提供一個一種叫 “位域” 或者 “位段” 的數(shù)據(jù)結(jié)構(gòu)。它的存在是為了更加的節(jié)省空間。因為在有些實際需求中,并不需要占用一個完整的字節(jié),而只是需要一個或者幾個二進制位。比如存在一個開關(guān)量時,只有 0 和 1 兩種狀態(tài),只需要一個二進制位存儲即可。
位域的定義如下:
struct 位域結(jié)構(gòu)名{ 位域列表 };
其中位域列表的形式為:類型說明符 位域名:位域長度
例如,
struct test{
int a:3;
int b:2;
int c:6;
};
位域的變量的說明 與結(jié)構(gòu)體的方式一樣,有三種方式:先定義后說明、同時定義說明、直接說明。
在聲明時,位段成員必須是整形或枚舉類型(通常是無符號類型)。
結(jié)構(gòu)中也可以包含無名位域,作為相鄰成員之間的填充或調(diào)整位置。無名位域無法被引用,它們的內(nèi)容在運行時是不可預(yù)測的。
struct s{
unsigned int a:4; //a存放結(jié)構(gòu)的前4個位中
unsigned int :2; // a后面的2個位填充
unsigned int b:2;
};
位域的定義有如下限制
1、一個位域必須存儲在同一個字節(jié)中,不能跨兩個字節(jié)。
比如當(dāng)一個字節(jié)所剩的空間不夠下一個位域存儲時,則從下一個存儲單元的起始位置開始存放;也可以專門讓某個位域從下一個存儲單元的起始位置開始。
struct s{
unsigned int a:4; //a存放結(jié)構(gòu)的前4個位中
unsigned int :2; // a后面的2個位填充
unsigned int b:6; // 剩余的2位不夠存儲,從下一個存儲單元開始存放
unsigned int :0; //空位域,把該unsigned int剩余空間自動全部填充0
unsigned int c:5; //從下一個單元存儲,也即是從下一個 unsigned int 開始
}ss;
在這個位域中,a 占第一個字節(jié)的前 4 位,a 后面 2 位進行填充,第一個字節(jié)剩余的 2 位不夠 b 存儲,因此 b 從下一個字節(jié)開始存儲,b 后面的位域填充 0,c 從下一個單元存儲,該下一個單元指的是下一個 unsigned int,因為空位域 0 會把 b 后面 unsigned int 所剩的空間全部填充 0, 因此 sizeof(ss) = 8。
2、位域可以是無名域,無名域就是類似于 unsigned int : 0; 或者 unsigned int : 2; 雖然兩者只是 0 和非 0 的區(qū)別,但是作用卻是不同的。若無名域的位數(shù)為 0,則下一個位域?qū)娭茝南乱粋€單元開始(這里的一個單元指的不是下一個字節(jié),而是跨過跨過一次數(shù)據(jù)類型的自然邊界);如果無名位域為非 0,則意味著這個無名位域占著空間,不能被使用。
struct test{
unsigned int a:3; //a占3位
unsigned int :0; //對于unsigned int 類型 a 后面的剩余未全填充0
unsigned int b:2; //b 從下一個unsigned int 類型開始
}tt;
在該例子中 a 占 unsigned int類型所占字節(jié)的3位,a 后面的無名域會把 a 后面的所有位填充 0,因此對于 unsigned int 類型所占的 4 字節(jié)空間,除 a 的 3 位外,其他位均為0,b 從下一個 unsigned int 類型開始,所以 sizeof(tt) 為 8。
struct test{
unsigned int a:3; //a占3位
unsigned int :2; //a后面的2個位填充
unsigned int b:2; // b 跟著無名域后面
}tt;
a后面無名域占 2 位,b跟在無名域后面,所以 a 和 b共占用一個字節(jié)。因此sizeof(tt) 為 4。
3、如果相鄰位域字段的類型相同,且其位寬之和小于類型的 sizeof 大小,則后面的字段將緊鄰前一個字段存儲,直到不能容納為止 。****
struct test {
unsigned char a:3;
unsigned char b:4;
} t;
a 和 b 所占的寬度之和小于 sizeof(unsigned char)大小,因此 a和b共同使用一個字節(jié),所以 sizeof(t) 為 1;
4、如果相鄰位域字段的類型相同,但其位寬之和大于類型的 sizeof 大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍 。****
struct test {
unsigned int a:15;
unsigned int b:20; //從下一個unsigned int開始
} t;
由于 a 和 b 寬度之和大于 sizeof(unsigned int)大小,因此b從下一個 unsigned int 處開始,所以 sizeof(t) 為 8。
5、整個結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。
struct test {
unsigned char a : 3;
unsigned char b : 4;
unsigned char c : 3;
unsigned int d : 5;
} t;
整個結(jié)構(gòu)體的總大小為 unsigned int 類型的整數(shù)倍。
6、如果相鄰的位域字段的類型不同,則各編譯器的具體實現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式。(跟編譯器有較大的關(guān)系,使用時要慎重,盡量避免)
struct test {
unsigned char a : 3;
unsigned char b : 4;
unsigned char c : 3;
unsigned int d : 5;
} t;
對于該類型,不壓縮時,a和b類型相同會占用一個字節(jié),c會占用一個字節(jié),d會單獨存放,不會和共占用一個字節(jié),因為整個結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍,因此 sizefo(t)為 8。
若壓縮時(gcc下),c 和 d 共占用一個字節(jié),因為整個結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍,因此 sizefo(t)為 4。
7、 如果位域字段之間穿插著非位域字段,則不進行壓縮;(不針對所有的編譯器,跟編譯器有較大的關(guān)系,使用時要慎重,盡量避免)
struct test{
unsigned int m: 12;
unsigned int ch;
unsigned int p: 4;
}t;
sizeof(t) 大小為 12。
8、當(dāng)使用有符號類型來定義位域,并且無意中使用到了正負(fù)(有意或者無意)特性時,可能出現(xiàn)不是想要的結(jié)果 。
struct test
{
char a : 2;
char b : 3;
char c : 3;
};
struct test t;
// 位域賦值
t.a = 0x3; // 11
t.b = 0x5; // 101
t.c = 0x2; // 010
printf("%d,%d,%d\\n", t.a, t.b, t.c); //結(jié)果為 -1, -3, 2
可見,當(dāng)為域的最高位是 1 的時候,會進行符號擴展,而且這也取決于編譯器的實現(xiàn),因此,為避免此類問題,最好使用無符號類型定義位域。
9、取地址操作符 & 不能應(yīng)用在位域字段上,因此不存在位域的指針。
10、位域字段不能是靜態(tài)成員。
11、不能用來指定位數(shù)的類型。
若 struct 成員是指針變量類型不能用來指定所占的位數(shù),在 64 位系統(tǒng)中指針固定占 8 字節(jié),在 32 位系統(tǒng)中指針固定占 4 字節(jié)。
若 struct 成員是 double 或 float 類型,不能指定位數(shù),否則編譯出錯,位域類型無效。
審核編輯:劉清
-
C語言
+關(guān)注
關(guān)注
180文章
7614瀏覽量
137249 -
編譯器
+關(guān)注
關(guān)注
1文章
1639瀏覽量
49197
發(fā)布評論請先 登錄
相關(guān)推薦
評論