最近看到一些朋友在交流結(jié)構(gòu)體對(duì)齊方面的一些問題,從他們的交談中隱隱約感覺有幾個(gè)朋友對(duì)結(jié)構(gòu)體成員的對(duì)齊理解上有點(diǎn)偏差,不能說完全不對(duì)吧,畢竟這是老生常談的問題了~
1、變量與內(nèi)存
首先我們要明確,在嵌入式C語言中變量是什么?其實(shí)所謂的變量就是一小段內(nèi)存。
當(dāng)你隨心所欲的在C程序中定義著各種變量,有沒有想過他們是如何被安排到相應(yīng)內(nèi)存上的?
好吧,其實(shí)他們是怎么安排的,并不需要程序員太多的操心,這個(gè)映射過程都是編譯器自動(dòng)給大家分配的,(可以借助動(dòng)態(tài)內(nèi)存的角度去看待這個(gè)分配問題),因?yàn)榇蟛糠肿兞吭谝欢▋?nèi)存區(qū)域上是可以任意選擇地址的,比如你定義一個(gè)全局的int gVar 變量,在不進(jìn)行特殊指定內(nèi)存位置的情況下,其編譯后所分配的內(nèi)存地址并不一定每次都是相同的;當(dāng)然,每次編譯完成便會(huì)確定下來,并且程序中對(duì)該變量的訪問,均會(huì)使用所確定下來的內(nèi)存地址。
既然變量的地址分配過程由編譯器自動(dòng)完成,但有時(shí)候我們想把一些或者某個(gè)變量放在固定的內(nèi)存地址處等,此時(shí)就需要通過一些語法來告訴編譯器該如何分配這些指定變量?jī)?nèi)存的分配,比如要做復(fù)位數(shù)據(jù)恢復(fù)等。
然而內(nèi)存的對(duì)齊問題也是對(duì)這些變量分配位置處理的一種方式,通常我們看到的align或者pack等就是來干預(yù)編譯器的這塊處理的。
2、結(jié)構(gòu)體對(duì)齊
理解了上面的一個(gè)思路,那么我們來分析分析結(jié)構(gòu)體對(duì)齊問題。
參考demo:
運(yùn)行結(jié)果:
以上編譯結(jié)果采用的是32位編譯器,默認(rèn)對(duì)齊方式是4個(gè)字節(jié),char類型占據(jù)1個(gè)字節(jié),int占據(jù)4個(gè)字節(jié),
下面簡(jiǎn)單分析一下結(jié)果:
1、默認(rèn)方式,采用4個(gè)字節(jié)對(duì)齊,那么char后面需要填充3個(gè)字節(jié),然后存放int類型,所以結(jié)構(gòu)體大小輸出為8。
2、1字節(jié)對(duì)齊方式,直接緊湊排列,很多人也叫不進(jìn)行對(duì)齊處理,所以輸出結(jié)果是5。
3、2字節(jié)對(duì)齊方式,其實(shí)和4個(gè)字節(jié)對(duì)齊是類似的,char按照2字節(jié)對(duì)齊,所以后面需要填充一個(gè)字節(jié),這樣int才能以兩字節(jié)對(duì)齊排布,此時(shí)整個(gè)結(jié)構(gòu)體占據(jù)6個(gè)字節(jié)。
4、4字節(jié)對(duì)齊方式與默認(rèn)對(duì)齊方式一致,最后看看8字節(jié)對(duì)齊,此時(shí)char類型與int類型完全能夠被8個(gè)字節(jié)容納,而該結(jié)構(gòu)體最大數(shù)據(jù)類型是4個(gè)字節(jié),所以char后面會(huì)預(yù)留3個(gè)字節(jié),進(jìn)行4字節(jié)對(duì)齊,然后放置int類型,此時(shí)與4字節(jié)對(duì)齊是一致的。那么一些朋友會(huì)問,是不是在上面的8字節(jié)例子中再加入一個(gè)char類型的成員,整個(gè)結(jié)構(gòu)體就會(huì)占據(jù)16個(gè)字節(jié)了呢?
其輸出結(jié)果如下:
結(jié)構(gòu)體所占據(jù)的字節(jié)是12個(gè),那是不是認(rèn)為8字節(jié)對(duì)齊沒有意義呢?我們?cè)倏匆粋€(gè)實(shí)驗(yàn):
此時(shí)double占據(jù)了8個(gè)字節(jié),按照前面的思路應(yīng)該是4+8+4,應(yīng)該最終結(jié)構(gòu)體的大小是16個(gè)字節(jié),而結(jié)果顯示:
輸出結(jié)果顯示24=8+8+8的形式,大家也可以直接采用打印結(jié)構(gòu)體成員地址的辦法查看是幾個(gè)字節(jié)對(duì)齊,有點(diǎn)暈,到底編譯器這一塊是怎么處理的呢?結(jié)論是:對(duì)齊字節(jié)數(shù) = min<當(dāng)前指定的pack值,最大成員所占字節(jié)大小>。
很多朋友其實(shí)研究到這個(gè)階段基本上就沒有再繼續(xù)探究了~嵌入式C語言一定要跟硬件結(jié)合理解~
3、內(nèi)存對(duì)齊
其實(shí)所謂的結(jié)構(gòu)體對(duì)齊,并不是簡(jiǎn)單的1個(gè)字節(jié)、兩個(gè)字節(jié)等多個(gè)字節(jié)的排列組合,而是在對(duì)應(yīng)對(duì)齊地址上分布。首先對(duì)齊需要解決的問題是什么 ? 即為啥需要對(duì)齊?
提高內(nèi)存的訪問效率,也可以說是受CPU等硬件方面的限制,按照特定的對(duì)齊地址進(jìn)行數(shù)據(jù)的訪問要快于跨非對(duì)齊地址的內(nèi)存訪問;并且有些平臺(tái)僅支持對(duì)齊方式訪問,非對(duì)齊方式會(huì)直接運(yùn)行錯(cuò)誤。
為了加快相關(guān)數(shù)據(jù)的正確訪問,編譯器會(huì)把相關(guān)的變量盡量的放到對(duì)齊的地址上,也就是默認(rèn)的對(duì)齊方式,比如CPU在偶數(shù)地址上訪問比較快,那么就會(huì)采用2個(gè)字節(jié)對(duì)齊的方式。
所以結(jié)構(gòu)體內(nèi)部成員并不是簡(jiǎn)單成員字節(jié)個(gè)數(shù)的對(duì)齊拼湊,而是讓結(jié)構(gòu)體成員落在對(duì)齊的地址上以便訪問。如下圖所示,當(dāng)進(jìn)行2字節(jié)對(duì)齊,如果只是簡(jiǎn)單的拼湊,兩種分布都是可以的,但是左側(cè)才是正確的2字節(jié)對(duì)齊方式,char成員變量的地址是2,int變量的地址是4,均為2字節(jié)的倍數(shù)。
總結(jié)一下: 結(jié)構(gòu)體對(duì)齊不再是簡(jiǎn)單的字節(jié)個(gè)數(shù)的拼湊,而是要與內(nèi)存地址進(jìn)行掛鉤~一般我們也可以理解為內(nèi)存地址分配是多少字節(jié)的倍數(shù),就是多少直接對(duì)齊~
審核編輯:劉清
-
嵌入式
+關(guān)注
關(guān)注
5141文章
19529瀏覽量
314936 -
cpu
+關(guān)注
關(guān)注
68文章
11033瀏覽量
215995 -
C語言
+關(guān)注
關(guān)注
180文章
7630瀏覽量
140328 -
編譯器
+關(guān)注
關(guān)注
1文章
1655瀏覽量
49887
發(fā)布評(píng)論請(qǐng)先 登錄
滲壓計(jì)在混凝土結(jié)構(gòu)體中的安裝指南

Allegro Skill布局功能--器件絲印過孔對(duì)齊介紹與演示

C語言中結(jié)構(gòu)體與聯(lián)合體的深度解析:內(nèi)存布局與應(yīng)用場(chǎng)景
圖解邊沿對(duì)齊,中心對(duì)齊PWM(可下載)
一種基于基礎(chǔ)模型對(duì)齊的自監(jiān)督三維空間理解方法

晶振的頻率偏差與解決方法

Orcad繪制原理圖的元器件對(duì)齊方法

ADR4520的負(fù)載調(diào)整度有點(diǎn)偏差是什么原因引起的?
KiCad的對(duì)齊工具不好用?

結(jié)構(gòu)體成員的順序會(huì)影響結(jié)構(gòu)體的大小嗎
ARM嵌入式系統(tǒng)中內(nèi)存對(duì)齊的重要性

評(píng)論