0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

對結(jié)構(gòu)體的對齊理解上有點(diǎn)偏差

冬至配餃子 ? 來源:最后一個(gè)bug ? 作者:bug菌 ? 2022-08-10 18:08 ? 次閱讀

最近看到一些朋友在交流結(jié)構(gòu)體對齊方面的一些問題,從他們的交談中隱隱約感覺有幾個(gè)朋友對結(jié)構(gòu)體成員的對齊理解上有點(diǎn)偏差,不能說完全不對吧,畢竟這是老生常談的問題了~

1、變量與內(nèi)存

首先我們要明確,在嵌入式C語言中變量是什么?其實(shí)所謂的變量就是一小段內(nèi)存。

當(dāng)你隨心所欲的在C程序中定義著各種變量,有沒有想過他們是如何被安排到相應(yīng)內(nèi)存上的?

好吧,其實(shí)他們是怎么安排的,并不需要程序員太多的操心,這個(gè)映射過程都是編譯器自動給大家分配的,(可以借助動態(tài)內(nèi)存的角度去看待這個(gè)分配問題),因?yàn)榇蟛糠肿兞吭谝欢▋?nèi)存區(qū)域上是可以任意選擇地址的,比如你定義一個(gè)全局的int gVar 變量,在不進(jìn)行特殊指定內(nèi)存位置的情況下,其編譯后所分配的內(nèi)存地址并不一定每次都是相同的;當(dāng)然,每次編譯完成便會確定下來,并且程序中對該變量的訪問,均會使用所確定下來的內(nèi)存地址。

既然變量的地址分配過程由編譯器自動完成,但有時(shí)候我們想把一些或者某個(gè)變量放在固定的內(nèi)存地址處等,此時(shí)就需要通過一些語法來告訴編譯器該如何分配這些指定變量內(nèi)存的分配,比如要做復(fù)位數(shù)據(jù)恢復(fù)等。

然而內(nèi)存的對齊問題也是對這些變量分配位置處理的一種方式,通常我們看到的align或者pack等就是來干預(yù)編譯器的這塊處理的。

2、結(jié)構(gòu)體對齊

理解了上面的一個(gè)思路,那么我們來分析分析結(jié)構(gòu)體對齊問題。

參考demo:

pYYBAGLzgjeAT8iJAAD1H7F0bjk340.pngpYYBAGLzgj2ARVKUAAC0Xb725aI681.png

運(yùn)行結(jié)果:

poYBAGLzglmACdHRAAAwvobA02U713.png

以上編譯結(jié)果采用的是32位編譯器,默認(rèn)對齊方式是4個(gè)字節(jié),char類型占據(jù)1個(gè)字節(jié),int占據(jù)4個(gè)字節(jié),

下面簡單分析一下結(jié)果:

1、默認(rèn)方式,采用4個(gè)字節(jié)對齊,那么char后面需要填充3個(gè)字節(jié),然后存放int類型,所以結(jié)構(gòu)體大小輸出為8。

2、1字節(jié)對齊方式,直接緊湊排列,很多人也叫不進(jìn)行對齊處理,所以輸出結(jié)果是5。

3、2字節(jié)對齊方式,其實(shí)和4個(gè)字節(jié)對齊是類似的,char按照2字節(jié)對齊,所以后面需要填充一個(gè)字節(jié),這樣int才能以兩字節(jié)對齊排布,此時(shí)整個(gè)結(jié)構(gòu)體占據(jù)6個(gè)字節(jié)。

4、4字節(jié)對齊方式與默認(rèn)對齊方式一致,最后看看8字節(jié)對齊,此時(shí)char類型與int類型完全能夠被8個(gè)字節(jié)容納,而該結(jié)構(gòu)體最大數(shù)據(jù)類型是4個(gè)字節(jié),所以char后面會預(yù)留3個(gè)字節(jié),進(jìn)行4字節(jié)對齊,然后放置int類型,此時(shí)與4字節(jié)對齊是一致的。那么一些朋友會問,是不是在上面的8字節(jié)例子中再加入一個(gè)char類型的成員,整個(gè)結(jié)構(gòu)體就會占據(jù)16個(gè)字節(jié)了呢?

pYYBAGLzgnOAR4kbAAA_oPyNI74889.png

其輸出結(jié)果如下:

pYYBAGLzgpSARzfuAAAupVKITM0562.png

結(jié)構(gòu)體所占據(jù)的字節(jié)是12個(gè),那是不是認(rèn)為8字節(jié)對齊沒有意義呢?我們再看一個(gè)實(shí)驗(yàn):

poYBAGLzgqeAFrKaAABIrxc_Zj4505.png

此時(shí)double占據(jù)了8個(gè)字節(jié),按照前面的思路應(yīng)該是4+8+4,應(yīng)該最終結(jié)構(gòu)體的大小是16個(gè)字節(jié),而結(jié)果顯示:

poYBAGLzgr6ABA9MAAAtrVniz38521.png

輸出結(jié)果顯示24=8+8+8的形式,大家也可以直接采用打印結(jié)構(gòu)體成員地址的辦法查看是幾個(gè)字節(jié)對齊,有點(diǎn)暈,到底編譯器這一塊是怎么處理的呢?結(jié)論是:對齊字節(jié)數(shù) = min<當(dāng)前指定的pack值,最大成員所占字節(jié)大小>。

很多朋友其實(shí)研究到這個(gè)階段基本上就沒有再繼續(xù)探究了~嵌入式C語言一定要跟硬件結(jié)合理解~

3、內(nèi)存對齊

其實(shí)所謂的結(jié)構(gòu)體對齊,并不是簡單的1個(gè)字節(jié)、兩個(gè)字節(jié)等多個(gè)字節(jié)的排列組合,而是在對應(yīng)對齊地址上分布。首先對齊需要解決的問題是什么 ? 即為啥需要對齊?

提高內(nèi)存的訪問效率,也可以說是受CPU等硬件方面的限制,按照特定的對齊地址進(jìn)行數(shù)據(jù)的訪問要快于跨非對齊地址的內(nèi)存訪問;并且有些平臺僅支持對齊方式訪問,非對齊方式會直接運(yùn)行錯(cuò)誤。

poYBAGLzgteADWrAAABvtqExg18425.png

為了加快相關(guān)數(shù)據(jù)的正確訪問,編譯器會把相關(guān)的變量盡量的放到對齊的地址上,也就是默認(rèn)的對齊方式,比如CPU在偶數(shù)地址上訪問比較快,那么就會采用2個(gè)字節(jié)對齊的方式。
所以結(jié)構(gòu)體內(nèi)部成員并不是簡單成員字節(jié)個(gè)數(shù)的對齊拼湊,而是讓結(jié)構(gòu)體成員落在對齊的地址上以便訪問。如下圖所示,當(dāng)進(jìn)行2字節(jié)對齊,如果只是簡單的拼湊,兩種分布都是可以的,但是左側(cè)才是正確的2字節(jié)對齊方式,char成員變量的地址是2,int變量的地址是4,均為2字節(jié)的倍數(shù)。

pYYBAGLzgu6AXO9DAABqWWJvUDc646.png

總結(jié)一下: 結(jié)構(gòu)體對齊不再是簡單的字節(jié)個(gè)數(shù)的拼湊,而是要與內(nèi)存地址進(jìn)行掛鉤~一般我們也可以理解為內(nèi)存地址分配是多少字節(jié)的倍數(shù),就是多少直接對齊~


審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5052

    文章

    18915

    瀏覽量

    300851
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10773

    瀏覽量

    210454
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7581

    瀏覽量

    135594
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1608

    瀏覽量

    48979
收藏 人收藏

    評論

    相關(guān)推薦

    你是否真的了解結(jié)構(gòu)占用了多少字節(jié)?

    結(jié)構(gòu)成員所占內(nèi)存空間大小一般情況下,如果想知道結(jié)構(gòu)成員的內(nèi)存占用情況需要:1、先用結(jié)構(gòu)在內(nèi)
    的頭像 發(fā)表于 06-04 08:04 ?302次閱讀
    你是否真的了解<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>占用了多少字節(jié)?

    深入理解 FPGA 的基礎(chǔ)結(jié)構(gòu)

    轉(zhuǎn)載地址:https://zhuanlan.zhihu.com/p/506828648 文章很詳細(xì)的介紹了FPGA的基礎(chǔ)結(jié)構(gòu),能更直觀的理解內(nèi)部結(jié)構(gòu)原理。對深入學(xué)習(xí)很有幫助。 以下是正文: 這一段
    發(fā)表于 04-03 17:39

    使用LSM6DSOWTR里面的temperature sensor,為什么temperature offset達(dá)到了±15℃的偏差?

    你好,我們想使用LSM6DSOWTR里面的temperature sensor,但是看到規(guī)格書里面對temperature sensor的描述,temperature offset達(dá)到了±15℃的偏差,這個(gè)地方有點(diǎn)無法理解,這個(gè)
    發(fā)表于 03-15 07:59

    求助,關(guān)于G031ADC結(jié)構(gòu)設(shè)置的幾個(gè)疑問求解

    本人在使用ADC時(shí)想使用多通道模式,所以便在CUBEMX上將十九個(gè)通道全部打開(包括三個(gè)內(nèi)部通道),生成代碼以后詳細(xì)看了一下結(jié)構(gòu)的配置發(fā)現(xiàn)有幾個(gè)疑惑, 1.ADC通道分為規(guī)則通道和注入通道,那么
    發(fā)表于 03-15 07:03

    經(jīng)典 C 語言編程,結(jié)構(gòu)和聯(lián)合體如何共用?

    結(jié)構(gòu) 結(jié)構(gòu)占用的內(nèi)存大小,首先和編譯器的系統(tǒng)位數(shù)有關(guān)系,類似于CPU是 64 bits 還是 32 bits 的情形;其次,結(jié)構(gòu)
    的頭像 發(fā)表于 01-11 18:24 ?1167次閱讀
    經(jīng)典 C 語言編程,<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>和聯(lián)合體如何共用?

    結(jié)構(gòu)與指針的關(guān)系

    在C語言中,結(jié)構(gòu)(Struct)是一種用戶自定義的數(shù)據(jù)類型,它允許您將不同類型的數(shù)據(jù)項(xiàng)組合在一起,以便形成一個(gè)更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。結(jié)構(gòu)可以
    的頭像 發(fā)表于 01-11 08:00 ?871次閱讀
    <b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>與指針的關(guān)系

    keil arm工程中結(jié)構(gòu)1字節(jié)對齊如何實(shí)現(xiàn)

    在Keil Arm工程中,結(jié)構(gòu)對齊方式可以通過使用特定的編譯器指令或者關(guān)鍵字來實(shí)現(xiàn)。結(jié)構(gòu)對齊
    的頭像 發(fā)表于 01-05 14:40 ?3120次閱讀

    ADE7858A測出來的有效值和實(shí)際值偏差有點(diǎn)大是什么原因?qū)е碌模?/a>

    盡管ADE7858A有校驗(yàn),但校驗(yàn)前測出來的有效值和實(shí)際值偏差有點(diǎn)大,目前找不到原因,希望知道的專家能給予指導(dǎo),謝謝各位! 具體數(shù)值如下: 輸入的實(shí)際電壓有效值為228v,但從VRMS寄存器里得到的數(shù)值換算后得到的值為217,相差11V,貌似
    發(fā)表于 12-26 06:03

    請問AD8651和AD8652具在性能上有什么區(qū)別,哪款性能更優(yōu)?

    請問 AD8651和AD8652具在性能上有什么區(qū)別,哪款性能更優(yōu)
    發(fā)表于 11-22 06:23

    安全存儲功能中使用的重要結(jié)構(gòu)

    安全存儲功能中使用的重要結(jié)構(gòu) 在整個(gè)安全存儲功能的操作過程中,存在一些很重要的結(jié)構(gòu),這些結(jié)構(gòu)體用于記錄或保存所有安全文件和dirf.db
    的頭像 發(fā)表于 11-21 14:36 ?438次閱讀
    安全存儲功能中使用的重要<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>

    嵌套的結(jié)構(gòu) 字節(jié)是如何對齊

    嵌套的結(jié)構(gòu),字節(jié)又是如何對齊的呢 先來看下面的代碼 typedef struct stu1 { char ary [ 5 ] ; int a; }stu1; typedef struct stu2
    的頭像 發(fā)表于 11-20 16:01 ?468次閱讀
    嵌套的<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b> 字節(jié)是如何<b class='flag-5'>對齊</b>的

    什么是結(jié)構(gòu)的字節(jié)對齊現(xiàn)象

    什么是結(jié)構(gòu)的字節(jié)對齊現(xiàn)象 程序員,咱都用代碼說話,先上 code: (說明:以下代碼均在 ARM 平臺上,使用 Keil 進(jìn)行編譯測試) # define offset_of (TYPE
    的頭像 發(fā)表于 11-20 15:55 ?529次閱讀
    什么是<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>的字節(jié)<b class='flag-5'>對齊</b>現(xiàn)象

    為什么我定義的結(jié)構(gòu)不能用--&gt;來調(diào)用結(jié)構(gòu)的元素?

    結(jié)構(gòu)代碼部分 typedef unsigned int uint_32t; typedef unsigned short uint_16t; typedef struct { uint_32t
    發(fā)表于 11-11 19:06

    當(dāng)電機(jī)的參數(shù)偏離時(shí),允許的偏差范圍是多少 偏差的相對基準(zhǔn)又如何選擇?

    當(dāng)電機(jī)的參數(shù)偏離時(shí),允許的偏差范圍是多少,偏差的相對基準(zhǔn)又如何選擇? 電機(jī)參數(shù)的偏差范圍是指在設(shè)計(jì)和制造電機(jī)過程中,一些參數(shù)可能會存在一定的誤差或偏離設(shè)計(jì)要求。這些參數(shù)的偏差范圍是由電
    的頭像 發(fā)表于 11-06 11:46 ?2676次閱讀

    求助,結(jié)構(gòu)變量定義引用問題求解

    |= mask; } 如以上語句,GPIO_Type是個(gè)結(jié)構(gòu)定義,定義了一個(gè)*base變量,在引用其中的成員時(shí),是base->IMR的方式。這怎么理解; 如果是
    發(fā)表于 10-27 06:06