python做網(wǎng)站快嗎關(guān)鍵詞智能調(diào)詞工具
? ? ? Hi~!這里是奮斗的小羊,很榮幸各位能閱讀我的文章,誠(chéng)請(qǐng)?jiān)u論指點(diǎn),歡迎歡迎~~? ? ?
????????????????????????????????????????????????💥個(gè)人主頁(yè):小羊在奮斗
????????????????????????????????????????????????💥所屬專欄:C語(yǔ)言? ?
????????本系列文章為個(gè)人學(xué)習(xí)筆記,在這里撰寫(xiě)成文一為鞏固知識(shí),二為一些學(xué)友們展示一下我的學(xué)習(xí)過(guò)程及理解。文筆、排版拙劣,望見(jiàn)諒。?
????????????????????????????????1、結(jié)構(gòu)體類型的聲明
????????????????????????????????2、結(jié)構(gòu)體內(nèi)存對(duì)齊
????????????????????????????????3、結(jié)構(gòu)體傳參
????????????????????????????????4、結(jié)構(gòu)體實(shí)現(xiàn)位段
1、結(jié)構(gòu)體類型的聲明
? ? ? ? 1.1結(jié)構(gòu)體變量的創(chuàng)建和初始化
? ? ? ? 其實(shí)之前在C語(yǔ)言(操作符)2中,我們已經(jīng)比較詳細(xì)地介紹過(guò)結(jié)構(gòu)體變量的創(chuàng)建和初始化,這里再補(bǔ)充一個(gè)特殊的初始化方法——按照指定的順序初始化。
? ? ? ? 前面我們學(xué)到的初始化方法是按結(jié)構(gòu)體成員的順序初始化,就像下面這樣:
? ? ? ? 除了按順序初始化,我們也可以按指定的順序初始化:
? ? ? ? 這兩種初始化方法得到的效果是一樣的。
? ? ? ? 1.2結(jié)構(gòu)體的特殊聲明
? ? ? ? 我們之前學(xué)過(guò)的結(jié)構(gòu)聲明常規(guī)形式是這樣的:
?????????但在聲明結(jié)構(gòu)的時(shí)候,還可以不完全聲明,就是省略掉自定義名。
? ? ? ? 但是這種不完全的結(jié)構(gòu)體聲明必須在聲明的同時(shí)直接創(chuàng)建變量,并且這個(gè)類型只能使用一次,也就是創(chuàng)建一次變量,但是一次可以創(chuàng)建多個(gè)。
? ? ? ? 下面這個(gè)代碼的問(wèn)題在哪兒呢?用這個(gè)不完全的結(jié)構(gòu)類型創(chuàng)建一個(gè)指針p,將p1的地址賦給p。
? ? ? ?當(dāng)我們運(yùn)行起來(lái)就會(huì)發(fā)現(xiàn)編譯器報(bào)警告,說(shuō)兩個(gè)指針類型不兼容。
? ? ? ? 這是因?yàn)槲覀儎?chuàng)建的結(jié)構(gòu)體類型是沒(méi)有名字的,雖然兩個(gè)成員一樣,但編譯器認(rèn)為它們兩個(gè)的地址類型是不一樣的。
? ? ? ? 1.3結(jié)構(gòu)的自引用
? ? ? ? 什么是結(jié)構(gòu)的自引用呢?說(shuō)白了就是結(jié)構(gòu)自己引用自己,有點(diǎn)遞歸的意思。舉個(gè)例子,當(dāng)我們想將一個(gè)數(shù)據(jù)存到內(nèi)存中時(shí),可以按順序存,也可以隨機(jī)地存,只要能找到就行。那當(dāng)我們隨機(jī)存的時(shí)候,找到第一個(gè)數(shù)怎么找到第二個(gè)數(shù)就是一個(gè)問(wèn)題。
? ? ? ? 這時(shí)候我們可以定義一個(gè)結(jié)構(gòu)體類型,有兩個(gè)成員名,一個(gè)存數(shù)據(jù),另一個(gè)存下一個(gè)數(shù)據(jù)的地址,這樣的話當(dāng)我們找到第一個(gè)數(shù)就能找到第二個(gè)數(shù),以此類推。
? ? ? ? 在結(jié)構(gòu)體的自引用過(guò)程中,夾雜了 typedef 對(duì)不完全結(jié)構(gòu)體類型聲明的重命名,也容易引出問(wèn)題。
? ? ? ? 上面的代碼是否正確呢?我們重命名了結(jié)構(gòu)體類型名,并且在結(jié)構(gòu)體成員中也用了重命名后的類型名。這樣做是不對(duì)的,因?yàn)槲覀兪且囟x這個(gè)結(jié)構(gòu)體類型的類型名,上面的代碼是在沒(méi)有重定義之前就使用了,打破了順序的問(wèn)題。
2、結(jié)構(gòu)體內(nèi)存對(duì)齊
? ? ? ? 2.1對(duì)齊現(xiàn)象?
? ? ? ? 在介紹之前我們可以先猜一下結(jié)構(gòu)體類型是怎么計(jì)算大小的呢?
? ? ? ?上面兩個(gè)結(jié)構(gòu)體類型成員變量相同,都是兩個(gè)char類型和一個(gè)int類型,那兩個(gè)結(jié)構(gòu)體類型的大小會(huì)是6個(gè)字節(jié)嗎?
? ? ? ? ?可以看到結(jié)果并不是我們猜測(cè)的,而且上面兩個(gè)結(jié)構(gòu)體類型只是改變了成員變量的順序,它們的大小就發(fā)生了變化。那我們可以得到的結(jié)論是,結(jié)構(gòu)體類型的大小并不是單純的成員變量類型大小之和,而且結(jié)構(gòu)體類型的大小還跟成員順序有關(guān)系。這是為什么呢?
? ? ? ? 其實(shí),結(jié)構(gòu)體的成員在內(nèi)存中是存在對(duì)齊現(xiàn)象的。
? ? ? ? ? ? ? ?
? ? ? ? 接著我們就來(lái)探討一下上面兩個(gè)結(jié)構(gòu)體類型的大小為什么是8個(gè)字節(jié)和12個(gè)字節(jié)。假設(shè)上面是一塊內(nèi)存,一小格表示一個(gè)字節(jié),第一個(gè)字節(jié)相較于起始位置偏移量為1,第二個(gè)字節(jié)相較于起始位置偏移量為2,以此類推,這就是偏移量的概念。
????????用結(jié)構(gòu)體類型 struct S1 創(chuàng)建一個(gè)結(jié)構(gòu)體變量s,假設(shè)s從第0個(gè)字節(jié)開(kāi)始,我們知道s的大小是8個(gè)字節(jié),那其成員n、c1、c2分別在哪個(gè)位置呢?這里再介紹一個(gè)宏 offsetof ,它的作用是計(jì)算結(jié)構(gòu)體成員相較于結(jié)構(gòu)體變量起始位置的偏移量。
?????????可以看到n的偏移量為0,c1的偏移量為4,c2的偏移量為5。也就是說(shuō)這三個(gè)結(jié)構(gòu)體成員在內(nèi)存是像下面這樣存的。
? ? ? ? 但是,這也不夠8個(gè)字節(jié)啊,難道說(shuō)結(jié)構(gòu)體變量s即使用不到第6、7個(gè)字節(jié)但還是將這兩個(gè)字節(jié)霸占了嗎?是的,這兩個(gè)字節(jié)就相當(dāng)于浪費(fèi)掉了。
? ? ? ? 那用結(jié)構(gòu)體類型 struct S2 創(chuàng)建的結(jié)構(gòu)體變量所占的12個(gè)字節(jié)里n、c1、c2三個(gè)成員變量是存在哪些位置呢??
? ? ? ??可以看到c2的偏移量為0,n的偏移量為4,c1的偏移量為8。也就是說(shuō)這三個(gè)結(jié)構(gòu)體成員在內(nèi)存中是像下面這樣存的。
? ? ? ? 可以看到上面也沒(méi)有12個(gè)字節(jié),并且把第1、2、3、9、10、11個(gè)字節(jié)都浪費(fèi)了。那這又是為什么呢?
? ? ? ? 通過(guò)上面的內(nèi)容我們可以得到的結(jié)論是,結(jié)構(gòu)體成員在內(nèi)存中存的時(shí)候并不是一個(gè)挨著一個(gè)存的,而是按一定的規(guī)則存儲(chǔ)的,這個(gè)規(guī)則就是結(jié)構(gòu)內(nèi)存對(duì)齊。?
2.2對(duì)齊規(guī)則
? ? ? ? 如果我們想要計(jì)算結(jié)構(gòu)體類型的大小,就必須要先了解結(jié)構(gòu)體的內(nèi)存對(duì)齊,才能知道結(jié)構(gòu)體類型在內(nèi)存中究竟是如何開(kāi)辟空間的。具體的對(duì)齊規(guī)則如下:
? ? ? ? (1)結(jié)構(gòu)體的第一個(gè)成員(不管是什么類型)對(duì)齊到和結(jié)構(gòu)體變量起始位置偏移量為0的地址處;
? ? ? ? (2)其他成員變量要對(duì)齊到某個(gè)數(shù)字(對(duì)齊數(shù))的整數(shù)倍的地址處;
? ? ? ? ???對(duì)齊數(shù) = 編譯器默認(rèn)的一個(gè)對(duì)齊數(shù) 與 該成員變量大小的較小值,VS中默認(rèn)的值為8,Linux中g(shù)cc沒(méi)有默認(rèn)對(duì)齊數(shù),對(duì)齊數(shù)就是成員自身的大小.
? ? ? ? (3)結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(結(jié)構(gòu)體中每個(gè)成員變量都有一個(gè)對(duì)齊數(shù),所有對(duì)齊數(shù)中最大的)的整數(shù)倍;
? ? ? ? (4)如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體成員對(duì)齊到自己的成員中最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體中成員的對(duì)齊數(shù))的整數(shù)倍。
? ? ? ? ?了解了對(duì)齊規(guī)則,我們就來(lái)解釋一下上面兩個(gè)結(jié)構(gòu)體類型的大小為什么是8個(gè)字節(jié)和12個(gè)字節(jié)。
? ? ? ? 先看結(jié)構(gòu)體類型 struct S1,根據(jù)規(guī)則(1),我們知道n存在第0、1、2、3這四個(gè)字節(jié)中。根據(jù)規(guī)則(2),VS默認(rèn)對(duì)齊數(shù)是8,c1的大小為1小于默認(rèn)對(duì)齊數(shù),c1要對(duì)齊到1的整數(shù)倍的地址處,所以c1存到了第4個(gè)字節(jié)中;再看c2,c2的大小也是1小于默認(rèn)對(duì)齊數(shù),c2要對(duì)齊到1的整數(shù)倍的地址處,所以c2存到了第5個(gè)字節(jié)中。根據(jù)規(guī)則(3),結(jié)構(gòu)體成員中最大對(duì)齊數(shù)為4,此時(shí)結(jié)構(gòu)體成員所占6個(gè)字節(jié),不是4的倍數(shù),所以還要再額外占用兩個(gè)字節(jié)的空間,那這兩個(gè)字節(jié)的空間就被浪費(fèi)掉了。所以最終這個(gè)結(jié)構(gòu)體類型的大小就是8個(gè)字節(jié)。
? ? ? ? 再看結(jié)構(gòu)體類型 struct S2,根據(jù)規(guī)則(1),我們知道c2存在第0個(gè)字節(jié)中。根據(jù)規(guī)則(2),VS默認(rèn)對(duì)齊數(shù)是8,n的大小為4小于默認(rèn)對(duì)齊數(shù),n要對(duì)齊到4的整數(shù)倍的地址處,所以n存到了第4、5、6、7個(gè)字節(jié)中,那第1、2、3個(gè)字節(jié)就被浪費(fèi)掉了;再看c1,c1的大小是1小于默認(rèn)對(duì)齊數(shù),c1要對(duì)齊到1的整數(shù)倍的地址處,所以c1存到了第8個(gè)字節(jié)中。根據(jù)規(guī)則(3),結(jié)構(gòu)體成員中最大對(duì)齊數(shù)為4,此時(shí)結(jié)構(gòu)體成員所占9個(gè)字節(jié),不是4的倍數(shù),所以還要再額外占用三個(gè)字節(jié)的空間,那這三個(gè)字節(jié)的空間也被浪費(fèi)掉了。所以最終這個(gè)結(jié)構(gòu)體類型的大小就是12個(gè)字節(jié)。
? ? ? ? 再來(lái)通過(guò)下面這個(gè)練習(xí)理解規(guī)則(4):
? ? ? ? 可以看到結(jié)構(gòu)體類型 struct S2 的大小是24個(gè)字節(jié),三個(gè)結(jié)構(gòu)體成員的偏移量分別為0、4和16。那么其成員變量在內(nèi)存中的存儲(chǔ)就應(yīng)該是這樣:
? ? ? ? 其中結(jié)構(gòu)體變量s的大小是8個(gè)字節(jié),其結(jié)構(gòu)體類型中成員變量最大對(duì)齊數(shù)為4,而對(duì)于結(jié)構(gòu)體類型 struct S2 其成員變量中最大對(duì)齊數(shù)為8,所以最終結(jié)構(gòu)體類型的大小就是24。?
? ? ? ? 2.3為什么存在內(nèi)存對(duì)齊
? ? ? ? 通過(guò)上面的學(xué)習(xí)我們知道內(nèi)存對(duì)齊的時(shí)候很容易就浪費(fèi)了內(nèi)存空間,那為什么還要存在內(nèi)存對(duì)齊呢??大部分的參考資料都是這樣說(shuō)的:
? ? ? ? (1)平臺(tái)原因(移植原因)
? ? ? ? 不是所有的硬件平臺(tái)都能訪問(wèn)任意地址上的任意數(shù)據(jù)的,某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),比如只能取int類型,那就只能訪問(wèn)4的倍數(shù)的內(nèi)存空間,否則拋出硬件異常。
? ? ? ? (2)性能原因
? ? ? ? 數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。原因在于,為了訪問(wèn)未對(duì)齊的內(nèi)存,處理器需要做兩次內(nèi)存訪問(wèn),而對(duì)齊的內(nèi)存訪問(wèn)僅需要一次訪問(wèn)。假設(shè)一個(gè)處理器總是從內(nèi)存中取8個(gè)字節(jié),則地址必須是8的倍數(shù)。如果我們能保證將所有的double類型的數(shù)據(jù)的地址都對(duì)齊成8的倍數(shù),那么就可以用一個(gè)內(nèi)存操作來(lái)讀或?qū)懥?/strong>。否則,我們可能需要執(zhí)行兩次內(nèi)存訪問(wèn),因?yàn)閷?duì)象可能被分在兩個(gè)8字節(jié)內(nèi)存塊中。
? ? ? ? 什么意思呢?假設(shè)創(chuàng)建一個(gè)結(jié)構(gòu)體類型,其中成員變量為char類型的c和int類型的n。
????????下面是不考慮對(duì)齊的情況,直接在c后面存n:
? ? ? ? 下面是考慮對(duì)齊的情況:
?
? ? ? ??因?yàn)閚為int類型,考慮對(duì)齊的話c后面三個(gè)字節(jié)的空間就浪費(fèi)掉了。
? ? ? ? 假設(shè)我們現(xiàn)在要用一個(gè)32位的機(jī)器去訪問(wèn)這個(gè)結(jié)構(gòu)體的成員變量n,32位的機(jī)器一次能訪問(wèn)4個(gè)字節(jié)的內(nèi)存,那在開(kāi)始位置訪問(wèn)不考慮對(duì)齊的情況時(shí)需要訪問(wèn)兩次才能讀取完整的n,但是在訪問(wèn)考慮對(duì)齊的情況時(shí)只需要訪問(wèn)一次就行了,因?yàn)閚前面剛好是4個(gè)字節(jié)可以跳過(guò)就沒(méi)有必要訪問(wèn)前面的內(nèi)存了,這樣的話效率就會(huì)提高。
? ? ? ??總的來(lái)說(shuō):結(jié)構(gòu)體的內(nèi)存對(duì)齊是拿空間來(lái)?yè)Q取時(shí)間的做法。
? ? ? ??那在設(shè)計(jì)結(jié)構(gòu)體的時(shí)候,我們既要滿足對(duì)齊,又要節(jié)省空間,如何做到呢?
? ? ? ? 有一個(gè)小技巧:讓占用空間小的成員盡量集中在一起。就像前面我們創(chuàng)建的 struct S1 和 struct S2 一樣,雖然兩個(gè)成員一樣,但是成員順序不一樣最終兩個(gè)結(jié)構(gòu)體類型的大小也就不一樣了。
?????????2.4修改默認(rèn)對(duì)齊數(shù)
? ? ? ? #pragma 這個(gè)預(yù)處理指令,可以改變編譯器的默認(rèn)對(duì)齊數(shù)。
? ? ? ? 上面結(jié)構(gòu)體類型 struct S 的大小在默認(rèn)對(duì)齊數(shù)下是12個(gè)字節(jié)。
????????當(dāng)我們將默認(rèn)對(duì)齊數(shù)改為1時(shí),結(jié)構(gòu)體類型 struct S 的大小就變成了6個(gè)字節(jié)。因?yàn)榻Y(jié)構(gòu)體成員c1、n、c2的大小分別為1、4、1,而默認(rèn)對(duì)齊數(shù)是1的時(shí)候,其每個(gè)成員的對(duì)齊數(shù)都為1,就相當(dāng)于沒(méi)有對(duì)齊,是緊挨著存儲(chǔ)的。
? ? ? ? 默認(rèn)對(duì)齊數(shù)一般修改的都是2的次方數(shù)。
3、結(jié)構(gòu)體傳參
? ? ? ? 來(lái)看下面的代碼:
#include <stdio.h>struct S
{int arr[1000];char ch;int n;
};void print1(struct S tmp)
{int i = 0;for (i = 0; i < 10; i++){printf("%d ", tmp.arr[i]);}printf("\n");printf("ch = %c\n", tmp.ch);printf("n = %d\n", tmp.n);
}void print2(struct S* tmp)
{int i = 0;for (i = 0; i < 10; i++){printf("%d ", tmp->arr[i]);}printf("\n");printf("ch = %c\n", tmp->ch);printf("n = %d\n", tmp->n);
}int main()
{struct S s = { {1,2,3,4,5,6,7,8,9,10}, 'a', 8 };print1(s);print2(&s);return 0;
}
? ? ? ? 上面寫(xiě)了兩個(gè)函數(shù)print1和print2打印結(jié)構(gòu)體變量s的內(nèi)容,print1用的是傳值調(diào)用,print2用的是傳址調(diào)用,哪個(gè)更好呢?
? ? ? ? 答案是傳址調(diào)用更好。傳值調(diào)用時(shí)形參tmp是拷貝了一份結(jié)構(gòu)體變量s,需要壓棧,要開(kāi)辟一塊和s大小相等的內(nèi)存空間,而且拷貝的過(guò)程也是需要時(shí)間的,所以時(shí)間和空間上都要消耗;而傳址調(diào)用指針tmp只需要接收一個(gè)4個(gè)字節(jié)或8個(gè)字節(jié)的地址就行,并不需要額外開(kāi)辟新的空間,并且還沒(méi)有拷貝過(guò)程中時(shí)間的消耗。
? ? ? ? 另外,傳值調(diào)用能做到的傳址調(diào)用都能做到,但是傳址調(diào)用能做到的傳值調(diào)用未必都能做到,比如間接修改內(nèi)存中的值。要是我們不想指針指向的對(duì)象被修改也可以加上const修飾,這樣就沒(méi)什么后顧之憂了。
? ? ? ? 所以,結(jié)構(gòu)體傳參的時(shí)候,要傳結(jié)構(gòu)體的地址。
4、結(jié)構(gòu)體實(shí)現(xiàn)位段
? ? ? ? 4.1什么是位段
? ? ? ? 位段的聲明和結(jié)構(gòu)體是類似的,有兩個(gè)不同:
? ? ? (1)位段的成員必須是 int、unsigned int 或 signed int,在c99中位段成員的類型也可以選擇其他類型。
? ? ? (2)位段的成員名后邊有一個(gè)冒號(hào)和一個(gè)數(shù)字。
? ? ? ? 來(lái)看結(jié)構(gòu)體和位段聲明的比較:
? ? ? ? 位段中的位指的是二進(jìn)制位也就是比特位,所以能想到的是位段中冒號(hào)后面的數(shù)字指的就是比特位,其中_a占2個(gè)比特位,_b占5個(gè)比特位,_c占10個(gè)比特位,_d占30個(gè)比特位。(成員名前面的_只是編程習(xí)慣沒(méi)有特殊意思)。而int型大小是4個(gè)字節(jié)最大32位,所以不能超過(guò)這個(gè)數(shù)。
? ? ? ? 為什么要有位段呢?
? ? ? ? 以前我們?cè)趯?xiě)代碼的時(shí)候,有沒(méi)有想過(guò)這樣一個(gè)問(wèn)題。就是說(shuō)我們創(chuàng)建了一個(gè)整型變量_a,它占4個(gè)字節(jié),但是這個(gè)_a我們只是想讓它表示0、1、2、3這四個(gè)值,而這四個(gè)值二進(jìn)制表示為00、01、10、11只需要2個(gè)比特位就行了,那我們還給它開(kāi)辟一個(gè)32位的空間是不是太浪費(fèi)了。同樣的如果_b只需要5個(gè)比特位就夠了,_c只需要10個(gè)比特位就夠了,_d只需要30個(gè)比特位就夠了,那我們就沒(méi)有必要給它們都開(kāi)辟4個(gè)字節(jié)的內(nèi)存空間了。
? ? ? ? 為了實(shí)現(xiàn)這種比較精準(zhǔn)的內(nèi)存大小的開(kāi)辟,位段就出現(xiàn)了。我們只需要按它們的需求開(kāi)辟相應(yīng)大小的內(nèi)存空間,就能避免很多不必要的浪費(fèi)。我們用上面聲明的結(jié)構(gòu)體和位段來(lái)做個(gè)驗(yàn)證,結(jié)構(gòu)體的大小是16個(gè)字節(jié),位段是不是真的變小了呢?
? ? ? ? 可以看到位段所占內(nèi)存是結(jié)構(gòu)體的一半。但是_a_b_c_d加起來(lái)一共是47個(gè)比特位,按道理來(lái)說(shuō)6個(gè)字節(jié)就夠了,為什么是8個(gè)字節(jié)呢?這跟位段的內(nèi)存分配有關(guān)。
? ? ? ? 4.2位段的內(nèi)存分配
? ? ? ? (1)位段的成員可以是 int、unsigned int、signed int 或者是 char 類型;
? ? ? ? (2)位段的空間上是按照需要以4個(gè)字節(jié)(int)或1個(gè)字節(jié)(char)的方式來(lái)開(kāi)辟的;
? ? ? ? (3)位段涉及很多不確定的因素,位段是不跨平臺(tái)的,注重可移植的程序應(yīng)避免使用位段。
? ? ? ? 那位段到底是如何分配內(nèi)存的呢?
? ? ? ? 以上面這個(gè)位段為例,char類型一次開(kāi)辟8個(gè)比特位,而a只需要占用3個(gè)比特位就行,但是這時(shí)候就有個(gè)問(wèn)題,a是從左往右存呢還是從右往左存呢(左右或上下都是一樣的,這里以左右為例),這是不確定的,不妨假設(shè)是從右向左存的。a存好占用了3個(gè)比特位,剩下的5個(gè)比特位還足夠存b,存好b后只剩下1個(gè)比特位不夠存c了,還需要再開(kāi)辟8個(gè)比特位,那這時(shí)候又有個(gè)問(wèn)題,剩下的那一個(gè)比特位是浪費(fèi)掉呢還是存一部分c呢,這也是不確定的,不妨我們?cè)偌僭O(shè)不夠存下一個(gè)數(shù)據(jù)的話就浪費(fèi)掉。在開(kāi)辟的第二個(gè)字節(jié)中存好c后還剩3個(gè)比特位不足以存d,還需要再開(kāi)辟一個(gè)字節(jié)存d,按我們的假設(shè)需要開(kāi)辟3個(gè)字節(jié)的空間。
? ? ? ? 開(kāi)辟好內(nèi)存空間后,就需要給相應(yīng)的成員變量賦值,10的二進(jìn)制表示是1010,但是a只有3個(gè)比特位,所以a中只存了010;12的二進(jìn)制表示是1100,所以b中存了1100;3的二進(jìn)制表示是11,所以c中存了00011;4的二進(jìn)制表示是100,所以d中存了0100。
? ? ? ? 如果真按如上我們假設(shè)的存儲(chǔ)方式存儲(chǔ),那在內(nèi)存中應(yīng)該是下面這樣:
?????????
? ? ? ? 可以看到,在當(dāng)前VS環(huán)境下,我們的分析是正確的。了解了位段的內(nèi)存分配,我們?cè)倩氐缴厦娴膯?wèn)題,為什么下面這個(gè)位段是8個(gè)字節(jié)而不是6個(gè)字節(jié)。
? ? ? ? 一個(gè)int類型是32位,存好_a_b_c需要17個(gè)比特位,剩下15個(gè)比特位顯然不足以存_d,所以還需要再開(kāi)辟4個(gè)字節(jié)的空間存_d,所以總共需要8個(gè)字節(jié),而不是6個(gè)字節(jié)。
? ? ? ? 4.3位段的跨平臺(tái)問(wèn)題
? ? ? ? (1)int 位段被當(dāng)成有符號(hào)數(shù)還是無(wú)符號(hào)數(shù)是不確定的;
? ? ? ? (2)位段中最大位的數(shù)目不能確定(16位機(jī)器最大16,32位機(jī)器最大32,寫(xiě)成27,在16位機(jī)器中會(huì)出問(wèn)題);
? ? ? ? (3)位段中的成員在內(nèi)存中從左向右分配,還是從右向左分配,標(biāo)準(zhǔn)尚未定義;
? ? ? ? (4)當(dāng)一個(gè)結(jié)構(gòu)包含兩個(gè)位段,第二個(gè)位段成員比較大,無(wú)法容納于第一個(gè)位段剩余的位時(shí),是舍棄剩余位還是利用,這是不確定的。
? ? ? ? ?總結(jié):跟結(jié)構(gòu)相比,位段可以達(dá)到同樣的效果,并且可以很好的節(jié)省空間,但是有夸平臺(tái)的問(wèn)題。
? ? ? ? 4.4位段使用的注意事項(xiàng)
? ? ? ? 位段的幾個(gè)成員可能共有同一個(gè)字節(jié)的地址,這樣有些成員的起始位置并不是某個(gè)字節(jié)的起始位置,那么這些位置處是沒(méi)有地址的。內(nèi)存中每個(gè)字節(jié)分配一個(gè)地址,一個(gè)字節(jié)內(nèi)部的比特位是沒(méi)有地址的。
? ? ? ? 所以不能對(duì)位段的成員使用&操作符,這樣就不能使用scanf直接給位段的成員輸入值,只能是先輸入放在一個(gè)變量中,然后賦值給位段的成員。
????????????如果覺(jué)得我的文章還不錯(cuò),請(qǐng)點(diǎn)贊、收藏 + 關(guān)注支持一下,我會(huì)持續(xù)更新更好的文章。???