日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C语言程序设计 | 结构体内存对齐,位段

發(fā)布時間:2024/4/11 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言程序设计 | 结构体内存对齐,位段 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在我們學(xué)習(xí)結(jié)構(gòu)體時,可能會碰到幾個難以理解的問題,一個是內(nèi)存對齊,一個是位段。所以我想分享一下我對這兩個問題的理解,來幫助大家更好的學(xué)習(xí)這兩個知識點。

內(nèi)存對齊

struct {char i;char k; int j; }s1;struct {char i;int j;char k; }s2;int main() {printf("%d\n", sizeof(s1));printf("%d\n", sizeof(s2));return 0; }

上面的兩個結(jié)構(gòu)體,看上去是完全一樣的,只有聲明變量時的順序不一樣, 那么它們的大小一樣嗎?

運行后我們發(fā)現(xiàn),它們兩個的大小竟然不同,而且如果我們將它們每一個的大小相加起來,得到的也應(yīng)該是6,不是上面的兩個值,那么這是因為什么呢?
這時,就牽扯到了一個叫做內(nèi)存對齊的東西。

  • 第一個成員在與結(jié)構(gòu)體變量偏移量為0的地址處。
  • 其他成員變量要對齊到某個數(shù)字(對齊數(shù))的整數(shù)倍的地址處。
  • 對齊數(shù) = 編譯器默認(rèn)的一個對齊數(shù)與該成員大小的較小值

    . vs的默認(rèn)值為8,linux默認(rèn)值為4(32位系統(tǒng)下由于數(shù)據(jù)總線只有32位,每次只能讀取4個字節(jié))

  • 結(jié)構(gòu)體總大小為最大對齊數(shù)(每個成員變量都有一個對齊數(shù))的整數(shù)倍。
  • 如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對齊到在自己的最大對齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對齊數(shù)(含嵌套結(jié)構(gòu)體的對齊數(shù))的整數(shù)倍
  • 光看文字的話很難看懂,所以下面我會畫幾幅圖來描述一下內(nèi)存對齊是如何運作的。

    struct {char i;char k; int j; }s1;


    因為第一個i與第二個j是相同類型,所以不存在偏移,k所占據(jù)的字節(jié)數(shù)比j大,所以偏移量為默認(rèn)對齊數(shù)和K的大小的最小值,所以需要偏移到四個字節(jié),所以總共占了八個字節(jié)的大小

    struct {char i;int j;char k; }s2;

    看完上面的幾個圖解,我們了解到如果要合理運用空間,就應(yīng)該把占用空間較小的成員盡量集中到一起。

    那么,問題來了,為什么要有內(nèi)存對齊呢?

    在我們能百度到的大部分資料上,都是這樣說的:

  • 平臺原因:不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的,某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
  • 性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問,而對齊的內(nèi)存訪問僅僅需要一次。(為對齊需要讀取多個總線周期)
  • 看文字的話,如果你還不懂,那我再來畫一幅圖

    對于沒對齊過的,我們?nèi)绻胱x取這個k,因為我們每次讀取四個字節(jié),所以第一次讀取的時候只讀取到了k的前三個字節(jié),第二次才能讀取到k的第四個字節(jié),然后將其進行重組,才能得到這個k。如果是對齊過的,我們就可以一次性讀取,雖然使用的空間變多了,但是速度也快了很多。

    所以內(nèi)存對齊的意義就是用空間來換取時間,而空間的價格較為低廉,所以內(nèi)存對齊的性價比是十分高的。

    對齊數(shù)的修改

    當(dāng)然,系統(tǒng)默認(rèn)的對齊數(shù)是不能適用于所有情況下的,所以我們可以修改對齊數(shù)來適用于我們所處的情景。

    我們可以適用一個預(yù)處理指令來修改默認(rèn)的對齊數(shù) # pragma pack(x), 這個x就填入我們想修改的數(shù)值


    如果我們想要只在一段使用這個對齊數(shù),而在下一段恢復(fù)的話,可以這樣使用


    位段

    講完了內(nèi)存對齊,下一個就來講講這個位段。

    什么是位段

    位段的聲明和結(jié)構(gòu)體是類似的,僅僅存在兩個地方的不同:

  • 位段的成員必須是int , unsigned int ,signed int, char
  • 位段的成員名后邊有一個冒號和一個數(shù)字
  • 例如:

    struct {int a : 1;int b : 3;int c : 5;int d : 31; }s3;

    那么,它的大小是多少呢?
    為什么會是8呢?
    因為一個整形是4個字節(jié),而4個字節(jié)有32個比特位,前三個我們分別給的是1,3,5,加起來總共是9,沒有達到32個比特位,而第四個占了31個,前三個已經(jīng)無法在存放這個31,所以這個31單獨存放在下一段空間中。總共占了兩個整形的空間,所以是2* 4,八個字節(jié)。

    位段的內(nèi)存分配

  • 位段的成員可以是int,unsigned int ,signed int, char類型
  • 位段的空間上是按照需要以四個字節(jié)(int)或者一個字節(jié)(char)的方式來開辟的。
  • 位段設(shè)計很多不確定因素,位段是不跨平臺的,所以可移植的程序應(yīng)該避免使用位段

  • 對于這個位段,空間是如何開辟的呢?
    因為一個字符型占據(jù)一個字節(jié),而一個字節(jié)有八個比特位,所以我們需要先知道它們的二進制值

    所有的二進制值我都寫出來了,同時用框框框起來的是因為二進制數(shù)的位數(shù)大于我們的位段數(shù),所以我們需要進行截斷

    這就是位段的存儲方式。

    位段的跨平臺問題

    上面的最后一點提到過對于跨平臺的程序應(yīng)該避免使用位段,這是為什么呢?

  • in位段被當(dāng)成有符號的還是無符號數(shù)是不確定的。
  • 位段中最大位的數(shù)目不能確定(16位機器最大16,32位機器最大32,寫成27,在16位機器會出現(xiàn)問題)
  • 位段中的成員在內(nèi)存中從左向右分配,還是從右向左分配標(biāo)準(zhǔn)尚未定義(這個就是我在上個月講的那個大小端的問題)
  • 當(dāng)一個結(jié)構(gòu)包含兩個位段,第二個位段成員比較大,無法容納于第一個位段剩余的位時,是舍棄剩余的位還是利用,這是不確定的
  • 跟結(jié)構(gòu)相比,位段可以達到相同的效果,雖然可以很好的節(jié)省空間,但是存在著跨平臺的問題。

    總結(jié)

    以上是生活随笔為你收集整理的C语言程序设计 | 结构体内存对齐,位段的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。