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

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

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

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

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

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

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

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

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

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

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

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

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

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

參考demo:

pYYBAGLzgjeAT8iJAAD1H7F0bjk340.pngpYYBAGLzgj2ARVKUAAC0Xb725aI681.png

運行結(jié)果:

poYBAGLzglmACdHRAAAwvobA02U713.png

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

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

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

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

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

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

pYYBAGLzgnOAR4kbAAA_oPyNI74889.png

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

pYYBAGLzgpSARzfuAAAupVKITM0562.png

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

poYBAGLzgqeAFrKaAABIrxc_Zj4505.png

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

poYBAGLzgr6ABA9MAAAtrVniz38521.png

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

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

3、內(nèi)存對齊

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

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

poYBAGLzgteADWrAAABvtqExg18425.png

為了加快相關(guān)數(shù)據(jù)的正確訪問,編譯器會把相關(guān)的變量盡量的放到對齊的地址上,也就是默認(rèn)的對齊方式,比如CPU在偶數(shù)地址上訪問比較快,那么就會采用2個字節(jié)對齊的方式。
所以結(jié)構(gòu)體內(nèi)部成員并不是簡單成員字節(jié)個數(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é)個數(shù)的拼湊,而是要與內(nèi)存地址進(jìn)行掛鉤~一般我們也可以理解為內(nèi)存地址分配是多少字節(jié)的倍數(shù),就是多少直接對齊~


審核編輯:劉清

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

    關(guān)注

    5083

    文章

    19131

    瀏覽量

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

    關(guān)注

    68

    文章

    10870

    瀏覽量

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

    關(guān)注

    180

    文章

    7605

    瀏覽量

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

    關(guān)注

    1

    文章

    1634

    瀏覽量

    49144
收藏 人收藏

    評論

    相關(guān)推薦

    ADR4520的負(fù)載調(diào)整度有點偏差是什么原因引起的?

    的負(fù)載調(diào)整度有點偏差(應(yīng)該是等效內(nèi)阻大吧),所以準(zhǔn)備用ADS1255資料推薦的OPA350做一級緩沖,不過發(fā)現(xiàn)有以下幾個問題讓我感覺到很困惑: 1.OPA350的失調(diào)電壓居然高達(dá)1mV(這個數(shù)值可以
    發(fā)表于 12-18 06:56

    KiCad的對齊工具不好用?

    “ ?不存在的。唯一的原因是您還沒有學(xué)會怎么用。 ? ” 對齊命令在哪里? KiCad的對齊命令(Align)藏得比較隱蔽,既不在菜單欄,也不在工具欄。右鍵的菜單中默認(rèn)也不存在。只有當(dāng)您 選中兩個或
    的頭像 發(fā)表于 12-04 18:15 ?149次閱讀
    KiCad的<b class='flag-5'>對齊</b>工具不好用?

    結(jié)構(gòu)成員的順序會影響結(jié)構(gòu)的大小嗎

    相同的結(jié)構(gòu)成員,如果把順序調(diào)整一下,會不會影響結(jié)構(gòu)的大??? 答案是會的,這主要跟字節(jié)對齊有關(guān)。 比如這樣的
    的頭像 發(fā)表于 11-25 16:24 ?182次閱讀

    ARM嵌入式系統(tǒng)中內(nèi)存對齊的重要性

    做嵌入式系統(tǒng)軟件開發(fā),經(jīng)常在代碼中看到各種各樣的對齊,很多時候我們都是知其然不知其所以然,知道要做好各種對齊,但是不明白為什么要對齊,不對齊會有哪些后果,這篇文章大概總結(jié)了內(nèi)存
    的頭像 發(fā)表于 11-11 17:17 ?871次閱讀
    ARM嵌入式系統(tǒng)中內(nèi)存<b class='flag-5'>對齊</b>的重要性

    聚徽觸控-工控一機(jī)和 PLC 一機(jī)有什么不同

    在工業(yè)自動化領(lǐng)域,工控一機(jī)和 PLC 一機(jī)都扮演著重要角色。盡管它們在某種程度上有所重疊,但它們在功能、應(yīng)用和設(shè)計結(jié)構(gòu)等方面存在顯著的不同。工控一
    的頭像 發(fā)表于 06-21 10:05 ?482次閱讀

    你是否真的了解結(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 ?478次閱讀
    你是否真的了解<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

    C語言結(jié)構(gòu)史上最詳細(xì)的講解【軟件干貨】

    struct結(jié)構(gòu)數(shù)據(jù)類型 前言 我們知道,在C語言中有一些基本的數(shù)據(jù)類型,如?char?int?float?long?double?string(c99) 等等數(shù)據(jù)類型,他們可以表示一些事物
    的頭像 發(fā)表于 03-28 17:52 ?774次閱讀

    STM32關(guān)于FLASH的編程對齊錯誤標(biāo)志位(PGAERR)的疑問求解

    大神們,我現(xiàn)在正在做一個應(yīng)用,需要熟悉STM32F4的FLASH的任何錯誤標(biāo)識,以用于特殊情況下的錯誤標(biāo)識判斷做相應(yīng)處理,但是針對FLASH的編程對齊錯誤標(biāo)志(PGAERR)與我理解不同。 原文
    發(fā)表于 03-22 07:59

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

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

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

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

    求助,請問一個結(jié)構(gòu)如何全部定義到 __attribute__ 區(qū)域?

    請問一個結(jié)構(gòu)如何全部定義到 __attribute__ 區(qū)域? 例如我這里涉及到一些高速計算的緩存,計劃將緩存數(shù)據(jù)存儲到 __attribute__ 區(qū)域。 三個結(jié)構(gòu) ,每個
    發(fā)表于 01-16 07:29

    經(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 ?1391次閱讀
    經(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ù)項組合在一起,以便形成一個更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。結(jié)構(gòu)可以
    的頭像 發(fā)表于 01-11 08:00 ?1005次閱讀
    <b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>與指針的關(guān)系

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

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