- 在某些機器上, 位段總是作為 unsigned 處理, 而不管它們是否被說明成 unsigned 的。
- 大多數C 編譯器都不支持超過一個字長的位段。
- 位段不可標明維數; 即, 不能說明位段數組, 例如 flag:l[2]。
- 最后, 不可以取位段地址。原因是, 在這種情況不, 顯然沒有稱作為 "位段指針" 類型的變量
file1:
http://hi.baidu.com/jevidyang/blog/item/5f2dc503d3c481763812bb6c.html
有些信息在存儲時,並不需要占用一個完整的字節,而只需占幾個或一個二進制位。例如在存放一個開關量時,只有0和1兩種狀態,用一位二進位即可。為了節省存儲空間,並使處理簡便,C語言又提供了一種數據結構,稱為“位域”或“位段”。
所謂“位域”是把一個字節中的二進位划分為幾個不同的區域,並說明每個區域的位數。每個域有一個域名,允許在程序中按域名進行操作。這樣就可以把幾個不同的對象用一個字節的二進制位域來表示。
1. 位域的定義和位域變量的說明
位域定義與結構定義相仿,其形式為:
struct 位域結構名
{ 位域列表 };
其中位域列表的形式為:
類型說明符位域名:位域長度
例如:
struct bs
{
unsigned a:8;
unsigned b:2;
unsigned c:6;
};
位域變量的說明與結構變量說明的方式相同。可采用先定義后說明,同時定義說明或者直接說明這三種方式。
例如:
struct bs
{
unsigned a:8;
unsigned b:2;
unsignedc:6;
}data;
說明data為bs變量,共占兩個字節。其中位域a占8位,位域b占2位,位域c占6位。
其存儲位置一般從右至左進行存儲即:data: c(6bits) | b(2bits) | a(8bits). 依編譯器而定,TMS320的DSP的CCS按右至左的順序。
對於位域的定義尚有以下幾點說明:
0)位段成員的類型必須指定為unsigned int類型;
1) 一個位域必須存儲在同一個字節中,不能跨兩個字節。如一個字節所剩空間不夠存放另一位域時,應從下一單元起存放該位域。也可以有意使某位域從下一單元開始。
例如:
struct bs
{
unsigned a:4
unsigned :0 /*空域*/
unsigned b:4 /*從下一單元開始存放*/
unsigned c:4
}
在這個位域定義中,a占第一字節的4位,后4位填0表示不使用,b從第二字節開始,占用4位,c占用4位。
2) 由於位域不允許跨兩個字節,因此位域的長度不能大於一個字節的長度,也就是說不能超過8位二進位。
3) 位域可以無位域名,這時它只用來作填充或調整位置。無名的位域是不能使用的。例如:
struct k
{
unsigned a:1
unsigned :2 /*該2位不能使用*/
unsigned b:3
unsigned c:2
};
從以上分析可以看出,位域在本質上就是一種結構類型,不過其成員是按二進位分配的。
2. 位域的使用
位域的使用和結構成員的使用相同,其一般形式為:
位域變量名·位域名
位域允許用各種格式輸出。
【例】
main(){
struct bs
{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1;
bit.b=7;
bit.c=15;
printf("%d,%d,%d\n",bit.a,bit.b,bit.c);
pbit=&bit;
pbit->a=0;
pbit->b&=3;
pbit->c|=1;
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);
}
上例程序中定義了位域結構bs,三個位域為a,b,c。說明了bs類型的變量bit和指向bs類型的指針變量pbit。這表示位域也是可以使用指針的。程序的9、10、11三行分別給三個位域賦值(應注意賦值不能超過該位域的允許范圍)。程序第12行以整型量格式輸出三個域的內容。第13行把位域變量bit的地址送給指針變量pbit。第14行用指針方式給位域a重新賦值,賦為0。第15行使用了復合的位運算符"&=",該行相當於:
pbit->b=pbit->b&3
位域b中原有值為7,與3作按位與運算的結果為3(111&011=011,十進制值為3)。同樣,程序第16行中使用了復合位運算符"|=",相當於:
pbit->c=pbit->c|1
其結果為15。程序第17行用指針方式輸出了這三個域的值。
file2:
http://blog.sina.com.cn/u/489e70e1010005tn
例如, 一個存放值 36 的字節是八個二進制數字的串: 可以表示成 00100100。 存入值24 的字節可以表示成 00010100。
有時, 我們希望不僅對字節進行操作, 也要能對位進行操作。例如, 用布爾真或假條件表示的標志, 在計算機中可用位來表示。
但是, 說明一個用作標志的普通變量至少要用一個字節---8 位, 而在某些計算機系統中則可能是 16 位。如果我們想在一個很大的表中存儲很多標志, 那么 "被浪費" 的內存空間是很可觀的。在 C 語言中, 一種方法是用叫做位段的構造類型來定義一個壓縮信息的結構。

2.位段的用法
先看一個例子: 我們需要用到五個變量。 假定, 其中三個用作標志, 稱為 f1, f2 和 f3。
第四個稱為 type, 取值范圍為 1 至 12。 最后一個變量稱為 index, 值的范圍為 0 至 500。
通常, 我們用下面的語句來說明這些變量:
char f1,f2,f3;
unsigned int type;
unsigned int index;
但是, 實際上標志 f1, f2, f3 分別只需要 1 位。變量 type 只需要 4 位, 而變量 index 只需要 9 位。 總共是 16位 ---- 2 個字節。我們用兩個字節就夠了。
我們可這樣來做:
struct packed_struct
{
unsigned int f1 :1;
unsigned int f2 :1;
unsigned int f3 :1;
unsigned int type :4;
unsigned int index :9;
};

這種方法的好處是, 定義成 packed_struct 類型的變量的位段, 可以如引用一般的結構成員一樣方便地引用。同時, 使用了更少的內存單元數。
我們已經定義了一個稱作為 packed_struct 的包含着位段的結構。現在, 我們象下面那樣定義一個稱作為 packet_data 的變量: struct packed_struct packed_data; 於是, 我們就可以用簡單的語句, 把 packed_data 的 type 位段設置為 7:
packed_data.type = 7; 類似地, 我們可以用下面的語句把這個位段的值設為 n:
packed_data.type = n; 我們不必擔心 n 的值太長, 以致不能放入 type 位段中, C 編譯器會自動地僅取出 n 的低四位, 把它賦值給 packed_data.type。取出位段的值也自動地處理的, 因此語句 n = packed_data.type; 將從 packed_data 中取出 type 位段, 並把它的值賦給 n。
在一般的表達式中可以使用位段, 此時, 位段自動地轉換成整數。因此, 表達式
i = packed_data.index/5+1; 是完全有效的。
在包含位段的結構中, 也可以包括 "通常的" 數據類型。因此, 如果我們想定義一個結構, 它包含一個 int, 一個 char, 和二個 1 位的標志, 那么, 下面的定義是有效的:
struct table_entry
{
int count ;
char c;
unsigned int f1 :1;
unsigned int f2 :1;
};
當位段出現在結構定義中時, 它們就被壓縮成字。如果某個位段無法放入一個字中, 那么該字的剩余部分跳過不用, 該位段被放入下一個字中。
使用位段時, 必須注意下列事項:
- 在某些機器上, 位段總是作為 unsigned 處理, 而不管它們是否被說明成 unsigned 的。
- 大多數C 編譯器都不支持超過一個字長的位段。
- 位段不可標明維數; 即, 不能說明位段數組, 例如 flag:l[2]。
- 最后, 不可以取位段地址。原因是, 在這種情況不, 顯然沒有稱作為 "位段指針" 類型的變量。
struct bits
{
unsigned int f1:1;
int word;
unsigned int f3:1;
};

那么, 位段是怎樣壓縮的呢? 由於成員 word 出現於其間, 故 f1, f3 不會壓縮在同一個字內。C 編譯器不會重新安排位段定義來試圖優化存儲空間。
可以指定無名位段, 使得一個字中的某些位被 "跳過"。因此, 定義:
struct x_entry
{
unsigned int type :4;
unsigned int :3;
unsigned int count :9;
};