前述
大家在平常的編程過程應(yīng)該會碰到各種奇葩的問題吧,反正我最近是碰到了一次,再此跟大家分享一下。事情的原因是我在程序中增加了一個變量,然后就會導(dǎo)致程序每次都會進(jìn)入異常。
示例代碼
我將代碼簡化了,使用兩個模塊來演示這個問題。第一個模塊是Dev模塊。
下面是Dev模塊的頭文件,Dev結(jié)構(gòu)體中有一個數(shù)組。
#include#include typedefstruct{ inta_[100]; charb_; }Dev; voidDevInit(Dev*c_this);
下面是Dev模塊的源文件代碼,里面只有一個memset。
#include"Dev.h" voidDevInit(Dev*c_this){ memset(c_this,0,sizeof(Dev)); }
第二個模塊是DevManager,相關(guān)數(shù)據(jù)結(jié)構(gòu)如下:
#include"Dev.h" #pragmapack(1) typedefstruct{ Devuart_; charnum_;//Addingthisvariablecausesacrash Deviic_; }DevManage; #pragmapack() DevManagedev_manage; voidDevManagerInit(void){ DevManage*c_this=&dev_manage; memset(c_this,0,sizeof(DevManage)); DevInit(&c_this->uart_); DevInit(&c_this->iic_); }
DevManage結(jié)構(gòu)體包含uart以及iic設(shè)備,以及我新加入的一個num_變量,由于新增了num_變量以及與之相關(guān)的業(yè)務(wù)會導(dǎo)致每次調(diào)試目標(biāo)板都會進(jìn)入異常。
嘗試解決異常問題
根據(jù)調(diào)試情況看,每次都會出現(xiàn)異常,說明是個小問題。就怕偶爾出現(xiàn)異常,不容易復(fù)現(xiàn)。
思路應(yīng)該非常清晰,出現(xiàn)異常時候查看LR寄存器的值,LR寄存器主要有兩個功能。
保存子程序返回地址。使用BL或BLX時,跳轉(zhuǎn)指令自動把返回地址放入r14中
當(dāng)異常發(fā)生時,異常模式的R14用來保存異常返回地址
根據(jù)LR寄存器的值,從而定位到是執(zhí)行DevInit(&c_this->iic_)函數(shù)中的memset導(dǎo)致的。
第一反應(yīng)是DevInit中傳入的對象可能為空,操作了非法內(nèi)存才導(dǎo)致的錯誤。于是又重新調(diào)試了一遍,發(fā)現(xiàn)DevInit中對象的地址并不為空,而且就是等于實體對象中設(shè)備的地址。
這一刻我陷入了深深的自我懷疑,memset難道不是這樣用的?難道不是傳入一個地址,清0,然后sizeof(DevManage)就完事了?
我這代碼怎么會出錯,memset就是這樣用的,天王老子來我也是對的,這樣的心理是不是也深度還原了碰到問題時的你們。
如果是你,該如何繼續(xù)...
救命稻草
有人說,匯編是最后的救命稻草。那我也嘗試抓住這根稻草,出現(xiàn)問題時如下圖:
問題主要出現(xiàn)紅色箭頭指向的這一行,經(jīng)過查詢資料得知_aeabi_memclr4是一個用于ARM嵌入式系統(tǒng)的函數(shù),用于將內(nèi)存區(qū)域清零。函數(shù)名中的"_a"表示該函數(shù)符合ARM嵌入式應(yīng)用二進(jìn)制接口(Embedded Application Binary Interface,EABI)規(guī)范。在調(diào)用_aeabi_memclr4時,需要確保傳入的內(nèi)存地址是四字節(jié)對齊的。再看圖片中的對象地址為0x200001BD,剛好比四字節(jié)對齊地址0x200001BC多了一個字節(jié)。
再回頭看DevManage對象,這里使用了偽指令#pragma pack(1)讓內(nèi)存分配進(jìn)行單字節(jié)對齊。因為Dev對象是按照四字節(jié)對齊的,緊接著引入了新的成員num_占用一個字節(jié)。所以導(dǎo)致iic_對象的地址相對于未增加變量之前的地址偏移了一個字節(jié),導(dǎo)致不是四字節(jié)對齊的了,從而引發(fā)了錯誤。
就示例中的代碼而言,只需要把強(qiáng)制DevManage對象單字節(jié)對齊的功能刪除即可解決問題,因為默認(rèn)情況下是四字節(jié)對齊的。
成員分配
由于#pragma pack(1)具有作用域限制,這里其實只是對num_變量做了單字節(jié)對齊的限制,而并沒有對Dev對象的內(nèi)存分配起到限制作用,Dev對象仍然是按照4字節(jié)對齊的(默認(rèn)值),所以Dev對象的所占用的長度一定是101*4=404字節(jié)。而整個DevManager對象的大小就101 * 4 + 1 + 101 * 4 = 809字節(jié),成員分配如下圖所示。
最后
到最后在拋出一個問題,對于上述的代碼在vscode中使用gcc編譯執(zhí)行為何沒有問題?
審核編輯:湯梓紅
-
模塊
+關(guān)注
關(guān)注
7文章
2771瀏覽量
49076 -
編程
+關(guān)注
關(guān)注
88文章
3673瀏覽量
94697 -
程序
+關(guān)注
關(guān)注
117文章
3817瀏覽量
82180 -
變量
+關(guān)注
關(guān)注
0文章
614瀏覽量
28757
原文標(biāo)題:加個變量,程序崩了
文章出處:【微信號:typedef,微信公眾號:typedef】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
數(shù)據(jù)波形中的一個數(shù)據(jù)異常點分析
分析一個關(guān)于STM32 芯片異常復(fù)位的經(jīng)典案例!
STM32局部變量過大導(dǎo)致棧溢出怎么去解決呢
請問main函數(shù)內(nèi)定義的變量是在棧上嗎?
XDATA定義變量后程序異常的原因?怎么解決?
多變量水質(zhì)參數(shù)時間異常事件檢測算法

讀取機(jī)器人程序中的變量

評論