閱讀673 返回首頁    go 阿裏雲 go 技術社區[雲棲]


嵌入式開發C語言位結構體用途詳解

嵌入式開發中,經常需要表示各種係統狀態,位結構體的出現大大方便了我們,尤其是在進行一些硬件層操作和數據通信時。但是在使用位結構體的過程中,是否深入思考一下它的相關屬性?是否真正用到它的便利性,來提高係統效率?

1.位結構體類型設計

[cpp] view plain copy print?

  1. //data structure except for number structure

  2. typedef struct symbol_struct

  3. {

  4. uint_32 SYMBOL_TYPE :5; //data type,have the affect on "data display type"

  5. uint_32 reserved_1 :4;

  6. uint_32 SYMBOL_NUMBER :7; //effective data number in one element

  7. uint_32 SYMBOL_ACTIVE :1;//symbol active status

  8. uint_32 SYMBOL_INDEX :8; //data index in norflash,result is related to "xxx_BASE_ADDR"

  9. uint_32 reserved_2 :8;

  10. }SYMBOL_STRUCT,_PTR_ SYMBOL_STRUCT_PTR;

分析:這裏定義了一個位結構體類型SYMBOL_STRUCT,那麼用該類型定義的變量都哪些屬性呢?

看下麵運行結果:

_1

WORDS是定義的另一個外層類型定義封裝,可以把它當作變量來看待。WORDS變量裏前5個數據域的地址都是0x1ffff082c,而reserved_2的地址0x1fff0830,緊接著的PressureState變量是0x1fff0834。

開始以為:reserved_1和SYMBOL_TYPE不在一個地址上,因為他們5+4共9位,超過了1個字節地址,但實際他們共用首地址了;而且reserved_2隻定義了8位,竟然實際占用了4個字節(0x1fff0834 - 0x1fff0830),我本來是想讓他占用1個字節的。WORDS整體占了8個字節(0x1fff0834 - 0x1fff082c),設計時分析占用5個字節

(SYMBOL_TYPE 1個;reserved_1 1個;SYMBOL_NUMBER+SYMBOL_ACTIVE 1個;SYMBOL_INDEX 1個;reserved_2 1個)。

uint_32 reserved_2 : 8; 占用4個字節,估計是uint_32在起作用,而這裏寫的8位,隻是我使用的有效位數,另外24位空閑,如果在下麵再定義一個uint_32 reserved_3 : 8,地址也是一樣的,都是以uint_32為單位取地址。

同理,上麵的5個變量,共用一個地址就不足為奇了。而且有效位的分配不是連續進行的,例如SYMBOL_TYPE+reserved_1 共9位,超過了一個字節,索性係統就分配兩個字節給他們,每人一個;SYMBOL_NUMBER+SYMBOL_ACTIVE 共8位,一個字節就能搞定。

2、修改數據結構,驗證上述猜想

[cpp] view plain copy print?

  1. //data structure except for number structure

  2. typedef struct symbol_struct

  3. {

  4. uint_8 SYMBOL_TYPE :5; //data type,have the affect on "data display type"

  5. uint_8 reserved_1 :4;

  6. uint_8 SYMBOL_NUMBER :7; //effective data number in one element

  7. uint_8 SYMBOL_ACTIVE :1; //symbol active status

  8. uint_8 SYMBOL_INDEX :8; //data index in norflash,result is related to "xxx_BASE_ADDR"

  9. uint_8 reserved_2 :8;

  10. }SYMBOL_STRUCT,_PTR_ SYMBOL_STRUCT_PTR;

地址數據如下:

_2

當換成uint_8後,可以看到地址空間占用大大減小,reserved_2隻占用1個字節(0x1fff069f - 0x1fff069e),其他變量也都符合上麵的結論猜想。但是,注意看上麵黃色和紅色的語句,總感覺有些勉強,那麼我又會想,前兩個變量數據域是9位,那麼他們實際上是不是真正的獨立呢?雖然在uint_8上麵他們是不同的地址,在uint_32的時候是不是也是不同的地址空間呢?

3、分析結構體內部的數據域是否連續,看下圖及結果

_3

本來假設: 由前2次試驗的結論,一共占用8個字節,節空間占用:(2+4)+(4+4)+(2+2+4)+(2+2)+(6)。可是,實際效果並不是想的那樣。實際隻占用了4個字節,係統並沒有按照預想的方式,為RESERVED變量分配4個字節。

分析:

這些數據域,整體相加一共32位,占用4個字節(不考慮數據對齊問題)。而實際確實是占用了4個字節,唯一的原因就是:這些數據域以緊湊的方式鏈接,沒有任何空閑位。實際是不是這樣呢?

看下圖和結果:

_4

這裏為了驗證是否緊湊鏈接,用到了一個union數據,後麵會講到用union不會對數據組織方式有任何影響,看實際與上次的一樣,也能分析出來。

主要是分析第2和第3個數據域是否緊密鏈接的。OBJECT_ACTIVE_PRE賦值0b00001111,NUMBER_ACTIVE賦值0b00000101,其他變量都是0,看到WORD數值0b1011111000000。分析WORD數據,可以看到這款MCU還是小端格式(高位數據在高端,低位數據在低端,這裏不對大小端進行討論),斷開數據變成(0)10111 11000000,正好是0101+1111,OBJECT_ACTIVE_PRE數據域,跨越了兩個字節,並不是剛開始設想的那樣。這就印證了上麵的緊密鏈接的結論,也符合數據結果輸出。

4、再次實驗,分析數據是否緊密鏈接,看下圖和結果

_5

可以看到,RESERVED數據域已經不再屬於4個地址空間內了(0x1fff0518 - 0x1fff051b),但是他們整體加起來還是32個位域。這說明數據中間肯定有“空隙”存在了,空隙在哪?看一下NUMBER_STATE,如果緊密的話它應該跟NUMBER_ACTIVE在同一個字節地址上,可是他們並不在一塊,“空隙”就存在這裏。

這兩個結構體有什麼不一樣?數據類型不一致,一個是uint_32,一個是uint_8。綜上所述:數據類型影響的是編譯器在分配物理空間時的大小單位,uint_32是以4個字節為單位,而後麵的位域則是指在已經分配好的物理空間內部再緊湊的方式分配數據位,當物理空間不能滿足位域時,那麼係統就再次以一定大小單位進行物理空間分配,這個單位就是上麵提到的uint_8或者uint_32。

舉例:上麵uint_32時,這些位域不管是不是在一個字節地址上,如果能夠緊湊的分配在一個4字節空間大小上,就直接緊湊分配。如果不能則繼續分配(總空間超過4字節),則再次以4字節空間分配,並把新的位域建立在新的地址空間上(條目1上的就是)。當uint_8時,很明顯如果位域不能緊湊的放在一個字節空間上,那麼就從新分配新的1字節空間大小,道理是一樣的。

5、結構體組合、共用體組合是否影響上述結論

_6

_7

可以看到,係統並沒有因為位結構體上麵有uint_4的4字節變量或者共用體類型,就改變分配策略把位域都擠到4字節之內,看來他們是沒有什麼實質性聯係的。這裏把uint_32改成uint_8,或者把位結構體也替換掉,經我試驗證明,都是沒有任何影響的。

嵌入式開發服務商朗銳智科www.lrist.com總結:

1、在操作位結構體時,要關注變量的位域是否在一個變量類型(uint_32或者uint_8)上,判斷占用空間大小

2、除了位域,還要關注變量定義類型,因為編譯器空間分配始終是按類型分配的,位域隻是指出了有效位(小於類型占用空間),而且如果位域大於類型空間,編譯器直接報錯(如 uint_8 test :15,可自行實驗)。

3、這兩個因素都影響變量占用空間大小,具體可以結合調試窗口,通過地址分配分析判斷

4、最重要的一點:上麵的所有結果,都是基於我自己的CodeWarrior10.2和MQX3.8分析出來的,不同的編譯環境和操作係統,都可能會有不同的結果;而且即便是環境相同,編譯器的配置和優化選項都有可能影響係統處理結果。結論並不重要,主要想告訴大家這一塊隱藏陷阱,在以後處理類似問題時,要注意分析避讓並掌握方法。

最後更新:2017-08-13 22:21:08

  上一篇:go  機器學習能為你的業務做什麼?有些事情你肯定猜不到!
  下一篇:go  淺談NB-IoT技術對農業物聯網發展的影響