一, 位段的解釋
下面是維基百科對位段的解釋:
??位段(或稱“位域”,Bit field)為一種數(shù)據(jù)結(jié)構(gòu),可以把數(shù)據(jù)以位的形式緊湊的儲存,并允許程序員對此結(jié)構(gòu)的位進行操作。這種數(shù)據(jù)結(jié)構(gòu)的好處:
- 可以使數(shù)據(jù)單元節(jié)省儲存空間,當程序需要成千上萬個數(shù)據(jù)單元時,這種方法就顯得尤為重要。
- 位段可以很方便的訪問一個整數(shù)值的部分內(nèi)容從而可以簡化程序源代碼。
??而位域這種數(shù)據(jù)結(jié)構(gòu)的缺點在于,其內(nèi)存分配與內(nèi)存對齊的實現(xiàn)方式依賴于具體的機器和系統(tǒng),在不同的平臺可能有不同的結(jié)果,這導致了位段在本質(zhì)上是不可移植的
二, 位段的聲明和使用
- 雖然位段可以決定用多少位來儲存數(shù)據(jù),但是切不可認為位段就是可以自定義一個數(shù)據(jù)類型。位段是依賴結(jié)構(gòu)體來實現(xiàn)的,我們可以認為位段是可以將一個盒子里面格子自定義大小。
位段的聲明:
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
需要注意的是:
- 這里面的數(shù)字代表的不是字節(jié),是比特(bit)。
- 位段成員的類型只能是整型家族的,例如:int, unsigned int, signed int, char。
位段的使用:
int main()
{
struct A a;
a._a = 2;
a._b = 3;
a._c = 5;
a._d = 10;
return 0;
}
相當于實例化后的a里面的不同大小的內(nèi)存里放入了數(shù)據(jù)。
三,位段的空間大小計算
因為不同平臺上的規(guī)則都是不太一樣的,計算出來的結(jié)果也會有些許差異,以下使用vs2022的x64環(huán)境下運行的
例如:
第一個例子:
#include <stdio.h>
struct A
{
int _a : 2;//二進制位
int _b : 5;
int _c : 10;
int _d : 30;
};
int main()
{
printf("%d", sizeof(struct A));
return 0;
}
上面代碼的輸出結(jié)果是8。
- 聲明類型是int類型的,所以一開始先開辟4個字節(jié)的內(nèi)存,也就是32bit。
- _a用掉了2bit,還剩下30bit。
- _b用掉了5bit,還剩下25bit。
- _c用掉了10bit,還剩下15bit。
- _d需要30bit的空間,但是預(yù)先開辟的空間只剩下15bit,所以我們還需要再開辟一個int大小的空間,之前剩下的15bit的空間選擇不使用,_d的30bit全放在第二個空間內(nèi)。
- 結(jié)果為8
第二個例子:
#include <stdio.h>
struct B
{
int _a : 30;//二進制位
int _b : 4;
int _d : 32;
};
int main()
{
printf("%d", sizeof(struct B));
return 0;
}
- 聲明類型是int類型的,所以一開始先開辟4個字節(jié)的內(nèi)存,也就是32bit。
- _a用掉了30bit,還剩下2bit。
- 由于只剩下2bit,_b需要4bit,所以舍棄2bit,再開辟一個32bit空間, _b用掉了4bit,還剩下28bit。
- 由于只剩下28bit,_d需要32bit,所以舍棄28bit,再開辟一個32bit空間,_d用掉了32bit
- 總共開辟了3次int類型的空間,所以結(jié)果為12
注意:
??大家有沒有發(fā)現(xiàn),我們在聲明位段的時候,如果定義的是int,那么冒號后面跟上的數(shù)字不能超過32,如果定義的是char,那么冒號后面跟上的數(shù)字不能超過8。如果超過以后,就會報出以下錯誤:
??其實根據(jù)內(nèi)存對齊原則,如果超出以后,處理器就需要訪問兩次才能完整的得到數(shù)據(jù)。所以在定義的時候,應(yīng)該避免超出應(yīng)有的內(nèi)存大小。
四, 位段的內(nèi)存分配
- 位段分配的內(nèi)存中的比特位是從左向右使用的,還是從右向左使用的呢?
- 如何證明內(nèi)存分配剩余的比特位不夠使用時,是繼續(xù)使用還是浪費掉呢?
接下來我們分析:
用例代碼:
#include <stdio.h>
struct A
{
char _a : 3;
char _b : 4;
char _c : 5;
char _d : 4;
};
int main()
{
struct A a = {0};
a._a = 10;
a._b = 12;
a._c = 3;
a._d = 4;
return 0;
}
我們假設(shè):位段分配的內(nèi)存中的比特位是從右向左使用的,分配剩余的比特位不夠使用時,浪費掉剩余內(nèi)存。
則:
- 我們先定義位段,如下圖:
- 執(zhí)行程序:
a._a = 10;
10的二進制為1010,放入_a中,由于_a只有3bit,需要截斷,所以舍棄最高位1,放入010:
- 執(zhí)行程序:
a._b = 12;
,12的二進制為1100,剛好可以放入,如下圖: - 執(zhí)行程序:
a._c = 3;
,3的二進制為11,由于_c有5bit,高位添0,放入00011,如下圖: - 執(zhí)行程序:
a._d = 4;
,4的二進制為100,放入0100,如下圖: - 程序就基本執(zhí)行完了,那么內(nèi)存中是什么樣的呢?根據(jù)上面分析,我們一開始給結(jié)構(gòu)體初始化為0,我們可以得到:
??由于機器是小端存儲,所以內(nèi)存上應(yīng)該是:62 03 04.
經(jīng)過調(diào)試,可以看到:
??以上也證明了, 在VS2022上,位段分配的內(nèi)存中的比特位是從右向左使用的,分配剩余的比特位不夠使用時,浪費掉剩余內(nèi)存,重新開辟新的空間。
當然,不同平臺得到的結(jié)果也可能會不同,這正是位段的缺點,可移植性差,接下來我們看看位段的跨平臺問題。
五,位段的跨平臺問題
- int 位段被當成有符號數(shù)還是無符號數(shù)是不確定的。
- 位段中最大位的數(shù)目不能確定。(16位機器最大16,32位機器最大32,寫成27,在16位機器會出問題)。
- 位段中的成員在內(nèi)存中從左向右分配還是從右向左分配的標準尚未定義。
- 當一個結(jié)構(gòu)包含兩個位段,第二個位段成員比較大,無法容納打一個位段剩余的位時,是舍棄剩余的位還是利用,這是不確定的。
總結(jié):跟結(jié)構(gòu)相比,位段可以達到同樣的效果,并且可以很好的節(jié)省空間,但是有跨平臺的問題存在。
六, 位段的應(yīng)用
位段由于跨平臺的問題,真正的用途的其中一個是計網(wǎng)的IP數(shù)據(jù)報:文章來源:http://www.zghlxwxcb.cn/news/detail-568178.html
? ?? 創(chuàng)作不易,你的點贊和關(guān)注都是對我莫大的鼓勵,再次感謝您的觀看??文章來源地址http://www.zghlxwxcb.cn/news/detail-568178.html
到了這里,關(guān)于【詳解】C語言冷門知識點之--位段的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!