C語(yǔ)言帶你學(xué)會(huì)位段相關(guān)知識(shí)
一、什么是位段
位段的聲明和結(jié)構(gòu)是類(lèi)似的,有兩個(gè)不同:
- 位段的成員必須是
int
、unsigned int
或signed int
- 位段的成員名后邊有一個(gè)冒號(hào)和一個(gè)數(shù)字
- 在下面,我分別寫(xiě)了一個(gè)結(jié)構(gòu)體和一個(gè)位段,注意看位段的寫(xiě)法和結(jié)構(gòu)體有什么不同
//結(jié)構(gòu)體 struct A { int a; int b; int c; int d; }; //位段 struct B{ int _a : 2; int _b : 5; int _c : 10; int _d : 30; };
然后我們sizeof
去計(jì)算一下這個(gè)結(jié)構(gòu)體的大小
printf("結(jié)構(gòu)體大?。?d\n", sizeof(struct A)); printf("位段大?。?d\n", sizeof(struct B));
可以看到,結(jié)構(gòu)體的大小是16,位段是8,二者為何會(huì)存在區(qū)別呢?原因在于這個(gè): 2
嗎?
- 那根據(jù)位段后面的這些數(shù)字,我們可以初步去斷定可能大小是這些數(shù)組的總和,再轉(zhuǎn)換為字節(jié)的。計(jì)算一下可以知道為
47b
,在內(nèi)存中1B = 8b
,要存下這個(gè)47個(gè)比特位的話應(yīng)該6個(gè)字節(jié)就夠了,但是結(jié)果為什么是8呢?我們不得而知??
學(xué)習(xí)了位段的相關(guān)知識(shí)后你就知道了
二、位段的內(nèi)存分配
首先來(lái)科普一下位段的相關(guān)知識(shí)??
- 位段的成員可以是 int unsigned int signed int 或者是 char (屬于整形家族)類(lèi)型
- 位段的空間上是按照需要以4個(gè)字節(jié)
[int]
或者1個(gè)字節(jié)[char]
的方式來(lái)開(kāi)辟的。 - 位段涉及很多不確定因素,位段是不跨平臺(tái)的,注重可移植的程序應(yīng)該避免使用位段
那從上面我們就可以提取出一些信息,知道了對(duì)于整型而言會(huì)開(kāi)辟出4個(gè)字節(jié)的數(shù)據(jù)給到位段作為存放,那接下去呢我們就來(lái)分析一下這個(gè)位段
- 仔細(xì)觀察可以得知每個(gè)成員都是整型,那首先開(kāi)辟出32個(gè)比特位
_a
占了2個(gè)比特位,還剩下【30b】_b
占了5個(gè)比特位,還剩下【25b】_c
占了10個(gè)比特位,還剩下【15b】_d
占了30個(gè)比特位,但是剩下的【15b】不夠用了,此時(shí)編譯器會(huì)繼續(xù)開(kāi)辟出4B,也就是32b的空間來(lái)存放
所以最后的結(jié)果就是4 + 4 = 8B
struct B{ //4Byte - 32bit int _a : 2; //30 int _b : 5; //25 int _c : 10; //15 //4Byte - 32bit int _d : 30; //4 + 4 = 8 };
看了我上面的這樣計(jì)算,你一定會(huì)有這些疑問(wèn)
?? 第一次是32b用剩后的【15b】去哪兒了呢?
?? _d
使用的是【15b】+ 后面開(kāi)辟出來(lái)的32b,還是只用到后面的32b呢?
?? 難道所有平臺(tái)都是這樣嗎?有沒(méi)有不一樣的計(jì)算方法?
上面是很多同學(xué)在課后提出來(lái)的疑問(wèn),關(guān)于這些,你在看完了我下面的分析后就會(huì)明白了?? 內(nèi)存圖分析位段分布
接下去我就通過(guò)對(duì)位段進(jìn)行分析,然后觀察內(nèi)存分布來(lái)揭曉上面究竟是如何計(jì)算的。
為了方便期間,這里換一組位段,但是換湯不換藥
struct S { char a : 3; char b : 4; char c : 5; char d : 4; }; int main(void) { struct S s = { 0 }; s.a = 10; s.b = 12; s.c = 3; s.d = 4; return 0; }
- 首先來(lái)看一下存放這個(gè)位段需要的字節(jié)數(shù)??梢钥吹竭@個(gè)位段中的每個(gè)成員都是
char
類(lèi)型的,所以編譯器會(huì)首先為其分配一個(gè)字節(jié)的空間,然后隨著變量的存入,最終是需要三個(gè)字節(jié)
然后我們來(lái)逐一分析一下??
- 剛才說(shuō)了,這個(gè)位段在內(nèi)存中需要開(kāi)辟三個(gè)字節(jié),這些變量要怎么存呢,首先看到變量a占了3個(gè)比特位,那是從左邊的三位開(kāi)始放還是右邊的三位呢?總不可以從中間開(kāi)始放吧!
那我們假設(shè)一下,從右邊往左邊放,那么a
放完后就是b
,占4個(gè)比特位,但是放c
的時(shí)候就放不下了,所以需要在開(kāi)辟1個(gè)字節(jié)的空間,此時(shí)d
再來(lái)放的話也放不下了,所以也要再開(kāi)辟1個(gè)字節(jié) ,最后也就需要3個(gè)字節(jié)的空間
【詳細(xì)分析如下】:
接下去我們就根據(jù)main函數(shù)中對(duì)位段各變量的初始化,來(lái)看看位段在內(nèi)存中的分布情況:a
的初始值為10,不過(guò)這是十進(jìn)制,轉(zhuǎn)換為二進(jìn)制形式的話就是[1010]
,轉(zhuǎn)看位段這里a變量的是占了3位,所以會(huì)截?cái)喑?code>010,將它放到第一個(gè)字節(jié)處的右邊3個(gè)比特位處
- 接下去是
b
,初始值為12
,轉(zhuǎn)換為二進(jìn)制形式的話就是[1100]
,而b在內(nèi)存中也剛好是占4個(gè)比特位的大小,剛好第一個(gè)字節(jié)處還可以放得過(guò),所以繼續(xù)順位放置 - 然后是
c
,初始值為3
,轉(zhuǎn)換為二進(jìn)制形式的話就是11
,但是c在內(nèi)存中也占5個(gè)比特位的大小,所以要在前面做一個(gè)擴(kuò)充便為[00011]
,但是第一個(gè)字節(jié)放不下了,上面放了【3】+【4】=【7】,只剩下1個(gè)比特位,那我們考慮再開(kāi)一個(gè)字節(jié)的空間,為了保持連續(xù)性就直接把這個(gè)5個(gè)比特位的數(shù)據(jù)放到第二個(gè)字節(jié)的右邊 - 最后的是
d
,初始值為4
,轉(zhuǎn)換為二進(jìn)制形式的話就是100
,不過(guò)d在內(nèi)存中也占4個(gè)比特位的大小,所以要在前面補(bǔ)上一個(gè)0,即為[0100]
,但是第二個(gè)字節(jié)也放不過(guò)了,只剩三個(gè)比特位了,所以我們考慮再開(kāi)一個(gè)字節(jié)的空間,然后放這個(gè)d
上面只是我假設(shè)的編譯器執(zhí)行思維,不過(guò)真正是怎樣的,我們還是要求證一下
- 那要怎么求證呢?這個(gè)很簡(jiǎn)單,既然這些變量都是存放在位段中,那我們剛才都算出所存放的二進(jìn)制形式了。對(duì)于內(nèi)存中的地址一般我們看到都是十六進(jìn)制,所以可以考慮把這些二進(jìn)制4個(gè)為一組轉(zhuǎn)換為十六進(jìn)制看看
01100010
即為——>0x62
00000011
即為——>0x03
00000100
即為——>0x04
- 而在內(nèi)存中左邊是低地址,右邊是高地址,所以我們看到的應(yīng)該是
62 03 04 cc
。來(lái)通過(guò)【內(nèi)存】觀察一下吧
可以看到,確實(shí)和我們分析得是一模一樣?
看完了上面這個(gè),相信你對(duì)一開(kāi)始的那個(gè)位段如何去進(jìn)行求解的整個(gè)流程應(yīng)該是非常清楚了,留給讀者自己的分析觀察??
三、位段的跨平臺(tái)問(wèn)題
接下去我們?cè)賮?lái)講講有關(guān)位段的跨平臺(tái)的問(wèn)題
- int 位段被當(dāng)成有符號(hào)數(shù)還是無(wú)符號(hào)數(shù)是不確定的
- 位段中最大位的數(shù)目不能確定。(16位機(jī)器最大16,32位機(jī)器最大32)
- 假設(shè)我們將位段中一個(gè)變量所占大小設(shè)置為30,即占30個(gè)比特位,那么它在32為機(jī)器上是沒(méi)問(wèn)題的,但是放到早期的16位機(jī)器上去的話,可能連編譯都編不過(guò),因?yàn)楦敬娣挪幌?/li>
- 位段中的成員在內(nèi)存中從左向右分配,還是從右向左分配標(biāo)準(zhǔn)尚未定義
- 剛才我分析的時(shí)候假設(shè)的是從右往左進(jìn)行分配,但是呢這在其他平臺(tái)上可能又是不一樣的了
- 當(dāng)一個(gè)結(jié)構(gòu)包含兩個(gè)位段,第二個(gè)位段成員比較大,無(wú)法容納于第一個(gè)位段剩余的位時(shí),是舍棄剩余的位還是利用,這是不確定的
- 這也就是我們一開(kāi)始糾結(jié)的【15】到底還用不用的問(wèn)題,這里給出解答,還是不確定,取決于平臺(tái)
總結(jié):跟結(jié)構(gòu)相比,位段可以達(dá)到同樣的效果,但是可以很好的節(jié)省空間,但是有跨平臺(tái)的問(wèn)題存在
- 這可能還有的老鐵不太理解,舉個(gè)例子:假設(shè)結(jié)構(gòu)體A中的這個(gè)變量
a
只可能有【0】【1】【2】【3】這四種取值,那么只需要2個(gè)比特位就可以表達(dá)這四個(gè)數(shù)字了,即【00】【01】【10】【11】,那我們便可以使用位段來(lái)是實(shí)現(xiàn):2
,但若是放在普通結(jié)構(gòu)體中的話就只能是一個(gè)整型4個(gè)字節(jié)32個(gè)比特位的大小,這也就浪費(fèi)了很多的空間 - 同理,若是變量
b
也只有5種表示形式的話,5個(gè)比特位就夠了,c
、d
也是一樣。那么這個(gè)時(shí)候位段就派上用場(chǎng)了,若是使用結(jié)構(gòu)體的話就會(huì)浪費(fèi)掉很多的空間。所以我們前面在看的時(shí)候,結(jié)構(gòu)體所占的空間大小是16B,而位段只有8B
四、位段的應(yīng)用
清楚了位段的相關(guān)知識(shí)和使用后,可能還是有同學(xué)比較迷惑這個(gè)位段到底是用來(lái)干嘛的,有什么實(shí)際應(yīng)用場(chǎng)景嗎?我們來(lái)看看
- 比方說(shuō)這里有個(gè)IP數(shù)據(jù)包,有學(xué)習(xí)過(guò)《計(jì)算機(jī)網(wǎng)絡(luò)》相關(guān)知識(shí)的讀者應(yīng)該都很清楚【不了解可以看看網(wǎng)絡(luò)層知識(shí)點(diǎn)匯總】,我們平常在網(wǎng)絡(luò)上和別人互相聊天的時(shí)候,所發(fā)送的消息并不是直接在網(wǎng)絡(luò)鏈路上進(jìn)行傳送的,而是會(huì)將其封裝到一個(gè)數(shù)據(jù)包中,它叫做IP數(shù)據(jù)包,例如我們所發(fā)送的
呵呵
只是里面的一個(gè)數(shù)據(jù)部分,還存在其他很多的字段,這些字段都占有各自的字節(jié)數(shù) - 其實(shí)對(duì)于這些字節(jié)數(shù)來(lái)說(shuō),就是使用【位段】來(lái)實(shí)現(xiàn),精準(zhǔn)地控制好每個(gè)字段需要多少字節(jié)數(shù),,就不會(huì)造成浪費(fèi)的現(xiàn)象了
五、總結(jié)與提煉
最后來(lái)總結(jié)一下本文所學(xué)習(xí)的內(nèi)容??
- 在本文中,我們首先講到了位段的相關(guān)概念,知道了原來(lái)使用結(jié)構(gòu)體還可以實(shí)現(xiàn)位段,不過(guò)在看了二者的大小后,卻產(chǎn)生了疑惑,為什么位段所占的大小是這些呢?
- 在清楚了位段在內(nèi)容中的相關(guān)分布后,我?guī)еx者一步步分析了位段中的成員數(shù)據(jù)到底是怎么一個(gè)個(gè)存放到內(nèi)存中的,也通過(guò)VS中的【內(nèi)存】驗(yàn)證觀察了我們的分析結(jié)果,是正確的
- 然后便說(shuō)道了位段這個(gè)東西其實(shí)具備很大的不確定性,因?yàn)樗嬖诳缙脚_(tái)的問(wèn)題,在不同平臺(tái)下實(shí)現(xiàn)的機(jī)制可能不同,所以就會(huì)導(dǎo)致最后的位段大小會(huì)不一致
- 最后,也說(shuō)道了位段的作用以及其實(shí)際的應(yīng)用場(chǎng)景,讓讀者學(xué)以致用
到此這篇關(guān)于C語(yǔ)言帶你學(xué)會(huì)位段相關(guān)知識(shí)的文章就介紹到這了,更多相關(guān)C語(yǔ)言位段知識(shí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c語(yǔ)言在控制臺(tái)判定鼠標(biāo)左鍵的小例子
c語(yǔ)言在控制臺(tái)判定鼠標(biāo)左鍵的小例子,需要的朋友可以參考一下2013-06-06C語(yǔ)言實(shí)現(xiàn)將彩色bmp圖像轉(zhuǎn)化為灰圖、灰度圖像反色
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)將彩色bmp圖像轉(zhuǎn)化為灰圖、灰度圖像反色,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10C++基于socket多線程實(shí)現(xiàn)網(wǎng)絡(luò)聊天室
這篇文章主要為大家詳細(xì)介紹了C++基于socket多線程實(shí)現(xiàn)網(wǎng)絡(luò)聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07詳談C++何時(shí)需要定義賦值/復(fù)制構(gòu)造函數(shù)
下面小編就為大家?guī)?lái)一篇詳談C++何時(shí)需要定義賦值/復(fù)制構(gòu)造函數(shù)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01C++中繼承與多態(tài)的基礎(chǔ)虛函數(shù)類(lèi)詳解
這篇文章主要給大家介紹了關(guān)于C++中繼承與多態(tài)的基礎(chǔ)虛函數(shù)類(lèi)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09C++ 回調(diào)接口設(shè)計(jì)和二進(jìn)制兼容詳細(xì)
再開(kāi)發(fā)視頻編輯 SDK,SDK的回調(diào)接口設(shè)計(jì)成 C 風(fēng)格,結(jié)構(gòu)中放著一些函數(shù)指針,既然對(duì)外接口是 C++,為什么不直接使用 C++ 的虛函數(shù)?這篇文章便對(duì)這一問(wèn)題做個(gè)詳細(xì)介紹,需要的朋友可以參考一下2021-09-09C語(yǔ)言學(xué)生成績(jī)管理系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言學(xué)生成績(jī)管理系統(tǒng)課程設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01