前言
之前在上一篇介紹linux kernel中build_on_zero的時候有使用到了bit fields技巧,然後我很懶的丟了一個超連結就代過了,後來越想越覺得自己對這個概念還沒是很清楚,所以痛定思痛來寫一篇介紹文順便把它搞懂。
Bit fields
bit fields是什麼?
簡單來說是可以在struct內以bit為單位來指定變數,考慮我們有一個需要紀錄4個flag的struct,如果都用bool(p.s. 題外話,c99後才在stdbool.h定義了bool這個type,我自首,我以前都沒注意…)來儲存的話則需要4*1byte = 4bytes。
但是其實每個flag只有true/false,也就是說只要一個bit來紀錄就可以了,所以我們可以用一個byte的4個bits來記錄這些flag,總共只佔了1byte。
用法如下,在struct中以冒號來指定bit field
struct bit_field_name |
注意在document有提到,bit field的type只能是以下這幾種:
- unsigned int (Ex: unsigned int b:3 ,代表b的範圍介於0..7)
- signed int(Ex: unsigned int b:3 ,代表b的範圍介於-4..3)
- int,注意這裡的int是implement-defined,也就是說它是有號無號的取決於compiler
- _Bool
下面直接舉例子一邊介紹比較相關的概念比較快。
Example1 - 使用方式
取自C - Bit Fields,當如果有兩個flag要設置的時候,使用bit field和不使用的差別。
/* define simple structure */ |
由於status1裡面有兩個unsigned int,所以大小是 2 * 4(unsigned int) = 8;而如果使用bit field則只有占用一個unsigned int,也就是4byte的空間。
Example2 - bit fields的對齊
bit fields的對齊可以使用unamed bit field來使得下一個bit field 對齊到下一個 unit 的 boundary,unamed bit field可以指定要pad的數量或是直接指定0對齊下一個unit,並且zero-width bit field 宣告不會使用到任何空間。
struct S { |
或是struct S {
// will usually occupy 8 bytes:
// 5 bits: value of b1
// 27 bits: unused
// 6 bits: value of b2
// 15 bits: value of b3
// 11 bits: unused
unsigned b1 : 5;
unsigned :0; // start a new unsigned int
unsigned b2 : 6;
unsigned b3 : 15;
};
int main(void)
{
printf("%zu\n", sizeof(struct S)); // usually prints 8
}
Example3 - 指定struct值
參照 C/C++ 位域 Bit fields 学习心得,bit fields struct的值除了可以一個一個變數指定外,也可以透過re-mapping的方式來達成。
int* p = (int *) &b1; // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址" |
也可以透過union來指定內容,透過在union內宣告一個和struct一樣大小的variable,透過指定該變數來初始化struct的記憶體空間。
Example4 - 指定struct值的注意事項
參照stackoverflow的這篇,考慮以下程式碼:
struct foo { |
在指定值的時候由於alignment,c跟d都已經取到了超出0xFFFF的記憶體空間了,所以可能會取得不正確的值。
Example5 - 應用實例
參閱Practical Use of Zero-Length Bitfields這篇的回答,當兩個平台所使用的規格不同的時候,有時為了相容,會進行alignment,這時候就會用到bit fields的技巧。
結語
最後,關於bit fields有幾點要注意的:
- 一個struct只有zero-width bit field是未定義行為
- 無法透過pointer去操作(Because bit fields do not necessarily begin at the beginning of a byte, address of a bit field cannot be taken)
- 不能用sizeof取得大小(但可以對整個struct做)
- Endianness影響了bit fields的順序(The order of bit fields within an allocation unit (on some platforms, bit fields are packed left-to-right, on others right-to-left)),這取決於compiler-dependent,參閱How Endianness Effects Bitfield Packing
- 雖然上面規定了使用的type,但其實也有人用char或unsigned char,參閱How does a bit field work with character types?
Reference
- document
- C - Bit Fields
- C/C++ 位域 Bit fields 学习心得
- How Endianness Effects Bitfield Packing
- How does a bit field work with character types?
- Practical Use of Zero-Length Bitfields