• 正文
    • 1、前言
    • 2、結(jié)構(gòu)體預(yù)留
    • 3、結(jié)構(gòu)體大小檢查
    • 4、結(jié)構(gòu)體成員相對(duì)偏移檢查
    • 5、總結(jié)
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

原來(lái)結(jié)構(gòu)體大小還可以這么檢查校驗(yàn)???

04/06 13:01
151
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

1、前言

相信不少朋友在編程的時(shí)候,都有用到過(guò)sizeof()關(guān)鍵詞得到結(jié)構(gòu)體的內(nèi)存大??;在開(kāi)發(fā)系統(tǒng)參數(shù)保存功能的時(shí)候,通過(guò)定義一個(gè)結(jié)構(gòu)體,將所有的系統(tǒng)參數(shù)都作為結(jié)構(gòu)體成員變量,然后保存。

結(jié)構(gòu)體保存的數(shù)據(jù)都是二進(jìn)制數(shù)據(jù),非常適合作為 MCU 的參數(shù)儲(chǔ)存方式,但是這種方式存在一個(gè)缺點(diǎn):擴(kuò)展性不高。

這種缺點(diǎn)一般通過(guò)結(jié)構(gòu)體成員空間預(yù)留的方式能得到解決。

2、結(jié)構(gòu)體預(yù)留

通常通過(guò)預(yù)留的方式進(jìn)行后期的參數(shù)擴(kuò)展,如:

typedef?struct
{
uint8_t?testParam;
uint8_t?testParam2;
uint8_t?reserve[6];????//?預(yù)留
}?TestParam_t;????/*?某模塊參數(shù)?*/

typedef?struct
{
uint8_t?testParam;
uint8_t?testParam2;
uint8_t?reserve1[10];???//?預(yù)留
TestParam_t?tTestParam;
uint16_t?testParam3;
uint8_t?reserve2[10];???//?預(yù)留
}?SystemParam_t;?/*?系統(tǒng)參數(shù)?*/

這種方式在預(yù)留位置擴(kuò)展新的成員變量時(shí),都要保證結(jié)構(gòu)體大小不變,且內(nèi)部的成員變量偏移位置不變,因?yàn)樵谠黾有碌某蓡T變量時(shí)由于結(jié)構(gòu)體填充的原因容易導(dǎo)致結(jié)構(gòu)體發(fā)生填充,從而不小心改變了結(jié)構(gòu)體大小,甚至改變了其他成員的偏移位置。

后果:在系統(tǒng)升級(jí)后讀取參數(shù)時(shí)就會(huì)因?yàn)榻Y(jié)構(gòu)體大小和升級(jí)前的不一致或者部分變量?jī)?nèi)存偏移改變引發(fā)系統(tǒng)異常。

所以在這種情況下,每次增加新的變量后都要仔細(xì)算一下結(jié)構(gòu)大小有沒(méi)有改變,甚至推算里面的結(jié)構(gòu)體成員相對(duì)偏移位置有沒(méi)有變化。

每次新增參數(shù),手動(dòng)計(jì)算和校驗(yàn) 99% 可以檢查出來(lái),但是人總有粗心的時(shí)候(加班多了,狀態(tài)不好…),且結(jié)構(gòu)體存在填充,一不留神就以為沒(méi)問(wèn)題,提交代碼,出版本(測(cè)試不一定能發(fā)現(xiàn)),給客戶,升級(jí)后異常,客戶投訴、扣工資(難啊….)

遇到這種問(wèn)題后:難道編譯器就沒(méi)有在編譯的時(shí)候檢查這個(gè)大小或者結(jié)構(gòu)體成員的偏移嗎,每次手動(dòng)計(jì)算校驗(yàn)好麻煩啊,一不留神還容易算錯(cuò)

哎,你別說(shuō),這種還真可以實(shí)現(xiàn)···

3、結(jié)構(gòu)體大小檢查

利用宏定義在編譯期間自動(dòng)檢查結(jié)構(gòu)體大小,在編譯的時(shí)候就能將錯(cuò)誤暴露出來(lái),宏定義如下:

/**
*?@brief?檢查結(jié)構(gòu)體大小是否符合,在編譯時(shí)會(huì)進(jìn)行檢查
*
*?@param?type?結(jié)構(gòu)體類型
*?@param?size?結(jié)構(gòu)體檢查大小
*/
#define?TYPE_CHECK_SIZE(type,?size)
extern?int?sizeof_##type##_is_error?[!!(sizeof(type)==(size_t)(size))?-?1]

使用方式:

typedef?struct
{
uint8_t?testParam;
uint8_t?testParam2;
uint8_t?reserve[6];????//?預(yù)留
}?TestParam_t;????/*?某模塊參數(shù)?*/

TYPE_CHECK_SIZE(TestParam_t,?8);?//?檢查結(jié)構(gòu)體的大小是否符合預(yù)期

在TestParam_t中增加一個(gè)變量,假設(shè)不小心預(yù)留大小寫錯(cuò)了:

typedef?struct
{
uint8_t?testParam;
uint8_t?testParam2;
uint16_t?testParam3;
uint8_t?reserve[5];????//?預(yù)留
}?TestParam_t;????/*?某模塊參數(shù)?*/

TYPE_CHECK_SIZE(TestParam_t,?8);?//?檢查結(jié)構(gòu)體的大小是否符合預(yù)期

編譯器報(bào)錯(cuò)內(nèi)容(通過(guò)sizeof_TestParam_t_is_error就能定位是哪個(gè)結(jié)構(gòu)體):

4、結(jié)構(gòu)體成員相對(duì)偏移檢查

利用宏定義在編譯期間自動(dòng)檢查結(jié)構(gòu)體中的成員變量偏移地址,在編譯的時(shí)候就能將錯(cuò)誤暴露出來(lái),宏定義如下:

/**
*?@brief?檢查結(jié)構(gòu)體成員偏移位置是否符合,?在編譯時(shí)會(huì)進(jìn)行檢查
*?@param?type?結(jié)構(gòu)體類型
*?@param?member?結(jié)構(gòu)體成員
*?@param?value?成員偏移
*/
#define?TYPE_MEMBER_CHECK_OFFSET(type,?member,?value)
extern?int?offset_of_##member##_in_##type##_is_error
[!!(__builtin_offsetof(type,?member)==((size_t)(value)))?-?1]

使用方式:

typedef?struct
{
uint8_t?testParam;
uint8_t?testParam2;
uint8_t?reserve[6];????//?預(yù)留
}?TestParam_t;????/*?某模塊參數(shù)?*/

TYPE_MEMBER_CHECK_OFFSET(TestParam_t,?testParam2,?1);

typedef?struct
{
uint8_t?testParam;
uint8_t?testParam2;
uint8_t?reserve1[10];???//?預(yù)留
TestParam_t?tTestParam;
uint16_t?testParam3;
uint8_t?reserve2[10];???//?預(yù)留
}?SystemParam_t;?/*?系統(tǒng)參數(shù)?*/

TYPE_MEMBER_CHECK_OFFSET(SystemParam_t,?tTestParam,?12);

在SystemParam_t中嘗試修改成員變量tTestParam的偏移位置檢查:

typedef?struct
{
uint8_t?testParam;
uint8_t?testParam2;
uint8_t?reserve1[10];???//?預(yù)留
TestParam_t?tTestParam;
uint16_t?testParam3;
uint8_t?reserve2[10];???//?預(yù)留
}?SystemParam_t;?/*?系統(tǒng)參數(shù)?*/

TYPE_MEMBER_CHECK_OFFSET(SystemParam_t,?tTestParam,?13);

編譯時(shí)則報(bào)錯(cuò):(通過(guò)offset_of_testParam2_in_TestParam_t_is_error就能定位是哪個(gè)結(jié)構(gòu)體的哪個(gè)成員變量偏移位置不對(duì)了):

5、總結(jié)

上述宏定義檢查方式是通過(guò)聲明一個(gè)數(shù)組,檢查正確則是數(shù)組[0],否則就是數(shù)組[-1],合理地利用編譯器規(guī)則(前提是編譯器支持定義數(shù)組[0])來(lái)檢查結(jié)構(gòu)體的大小和成員變量的偏移。

這個(gè)寫法而且只占用文本大小,編譯后不占內(nèi)存?。?!

關(guān)于這種方式的檢查,你了解或者能理解多少呢?

相關(guān)推薦