• 正文
    • 布置場景
    • 問題是怎么發(fā)生的?
    • 原因是什么呢?
    • 如何處理呢?
  • 推薦器件
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

C語言為什么使用指針會導致死機?

2024/06/26
2409
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

STM32系列的M0內(nèi)核剛出來的時候,為了降本,我有很多項目都需要移植到M0平臺,本以為只是把底層驅(qū)動看一下就可以,卻不想總是遇到莫名其妙的死機,著實折騰過一段時間的。今天在DIY遙控器的時候,突然又發(fā)現(xiàn)了一個當時遇到的問題,記錄下來給大家提個醒,萬一遇到的死機的情況,不妨思考一下這個問題。

布置場景

因為我的遙控器是基于無線RF通信的,因此我需要定義了兩個幀結(jié)構(gòu),分別用數(shù)組來存放,然后再發(fā)送和接收函數(shù)中直接處理數(shù)組。幀結(jié)構(gòu)的前四個字節(jié)作為通信的地址,也就是一個過濾ID,不符合這個ID的幀直接放棄掉。為了讓系統(tǒng)中兼容更多的設(shè)備對,我把這個通信地址定義為4個字節(jié),也就是一個uint32_t類型。數(shù)組及其索引值定義如下:

// 接收緩沖區(qū)長度定義#define DW_RX_MSG_LEN           18// 接收包中的地址索引#define    RX_ADDRESS_LL           0#define    RX_ADDRESS_LH           1#define    RX_ADDRESS_HL           2#define    RX_ADDRESS_HH           3...
uint8_t g_dw_rx_msg[DW_RX_MSG_LEN] = {0};  //接收緩存定義

接收緩沖器如上定義完成后,我將在接收函數(shù)中處理這個接收到的幀,第一件事情肯定是比對地址是否匹配,假設(shè)我宏定義的地址為:

#define  ADDRESS  0x12345678

最簡單的寫法肯定是這樣判斷:

if((g_dw_rx_msg[RX_ADDRESS_LL] == (ADDRESS >> 0) & 0xFF) &&   (g_dw_rx_msg[RX_ADDRESS_LH] == (ADDRESS >> 8) & 0xFF) &&   (g_dw_rx_msg[RX_ADDRESS_HL] == (ADDRESS >> 16) & 0xFF) &&   (g_dw_rx_msg[RX_ADDRESS_HH] == (ADDRESS >> 24) & 0xFF)    ){
}

上面這種寫法很明了,我們比對每一個字節(jié)是否相等,如果都相等,我們認為這個幀是發(fā)送給我們的,但是這樣的寫法比較啰嗦,字數(shù)比較多,并且并不直觀呀,好好的一個32bit的數(shù)據(jù),非要拆分成4次比較,干起來效率一點都不高。

問題是怎么發(fā)生的?

C語言中一定要用指針才顯得高級,這里很明顯可以使用指針來取出數(shù)組的前四個字節(jié),然后和我們的宏定義地址相比較,這樣是非常簡單高效的,大牛們常常都是這么寫的。

if(*(uint32_t *)g_dw_rx_msg == ADDRESS){}

你看多簡單,數(shù)組的名稱就是數(shù)組的地址,也是第一個元素的地址,我們?nèi)〕鰜磙D(zhuǎn)換成32bit整型,然后取出來直接和我們的地址比較。既能清楚的表達這是一個地址的比對,又能省不少敲字符的力氣。這樣也許在CortexM3上面大概率不會出現(xiàn)問題,但是在M0內(nèi)核上大概率會出問題,會死機的。為什么總說是概率呢?我先來描述一下你可能遇到的現(xiàn)象。我按照指針的寫法編譯通過了,運行也沒有問題,性能杠杠的,賊穩(wěn)定。后來,突然增加了一個需求,于是我又定義了一些變量和數(shù)組,再編譯,運行….

我擦,死機了!我就一直在新增的功能那里不斷地查找問題,怎么也找不到那里寫的有問題,只要我新定一個變量,并且在程序中使用它(防止編譯器優(yōu)化掉),就會死機,去掉就沒事了。調(diào)試過程中,你一定遇到過這樣的場景。當我們Debug一步一步的跟蹤時,就會發(fā)現(xiàn),程序死機之前執(zhí)行了我們的判斷語句。

原因是什么呢?

我們都知道,CortexM0內(nèi)核是一個32位總線的,它的硬件無法處理32位未對齊的內(nèi)存訪問,那么他對于內(nèi)存的訪問地址一定是4的整數(shù)倍,否則就會出現(xiàn)無法訪問內(nèi)存的問題。這里,我們的判斷語句中使用了指針的強制類型轉(zhuǎn)換(uint3_t*),那么如果我們數(shù)組中的元素地址恰好不是4的整數(shù)倍的時候,在轉(zhuǎn)化成uint3_t進行訪問時,處理器就只能報錯了。

為什么出錯是概率性的呢?編譯器會根據(jù)我們定義的全局變量和靜態(tài)變量來安排變量的地址,并且數(shù)組這東西,如果定義成uint8_t類型的,他中間的某個元素地址對齊的可能性只有四分之一。也就是說,我們第一次編譯成功,運行可靠的時候,恰好我們定義的全局變量合適,編譯器把我們的數(shù)組起始地址正好安排在32位對齊的地址上。當我們再定義一個變量的時候,一定是定義了非32位對齊的,比如uint8_t,或者一個數(shù)組或者結(jié)構(gòu)體,總之,它不是4字節(jié)的整數(shù)倍。這樣,我們再編譯,編譯器就會重新給我們安排所有變量的地址,我們的數(shù)組的地址就恰好給擠到了一個地址不對齊的地方。我們再用32bit的指針類型去訪問時,就會死機了。

如何處理呢?

有三種方法處理。第一種,要承認傻人有傻福,笨辦法有笨辦法的優(yōu)點,有些事情投機取巧可能會賺,但大概率還是賠的。就好比我們的大A,你以為是投資,后來認為是投機,再后來,你以為是詐騙,現(xiàn)在你可能認為是搶劫了,其實,我們的大A本質(zhì)上是捐款!跑題了,所以,我們老老實實按字節(jié)比較就可以了。

if((g_dw_rx_msg[RX_ADDRESS_LL] == (ADDRESS >> 0) & 0xFF) &&   (g_dw_rx_msg[RX_ADDRESS_LH] == (ADDRESS >> 8) & 0xFF) &&   (g_dw_rx_msg[RX_ADDRESS_HL] == (ADDRESS >> 16) & 0xFF) &&   (g_dw_rx_msg[RX_ADDRESS_HH] == (ADDRESS >> 24) & 0xFF)    ){
}

第二種,如果你實在忍受不了這個樣式,想讓程序更直觀的體現(xiàn)出來,它是一個地址的比對,那么就自己寫一個函數(shù),將四個字節(jié)取出來組成一個32bit的整型數(shù)?;蛘?,你可以直接利用memcpy函數(shù)來實現(xiàn)。

uint32_t my_address = 0;memcpy(&my_address, &((uint32_t*)g_dw_rx_msg), sizeof(uint32_t));

第三種,如果你還是糾結(jié)于優(yōu)雅和效率,那我們就不撞南墻不回頭,編譯器自身提供了一些指令,來強制我們定義的變量地址對齊。比如:__attribute__((aligned(4))) ?如下定義:

__attribute__((aligned(4))) uint8_t g_dw_rx_msg[DW_RX_MSG_LEN] = {0};  //接收緩存

按照上面的定義方法,我們的數(shù)組其實地址就會被安排在32位地址對齊的位置上了,指針就可以大顯身手了,不過要注意,如果我們的地址索引在數(shù)組內(nèi)部不對齊也是不行的。比如,我們的地址是從數(shù)組的第1個地址開始的。(前面有一個第0個)

// 接收緩沖區(qū)長度定義#define DW_RX_MSG_LEN           18// 接收包中的地址索引#define    RX_ADDRESS_LL           1     //這里地址從數(shù)組的第1個元素開始#define    RX_ADDRESS_LH           2#define    RX_ADDRESS_HL           3#define    RX_ADDRESS_HH           4...
__attribute__((aligned(4))) uint8_t g_dw_rx_msg[DW_RX_MSG_LEN] = {0};  //接收緩存
if(*(uint32_t *)g_dw_rx_msg == ADDRESS){    // 我還得死啊?。。。?/code>}

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風險等級 參考價格 更多信息
NLVVHC1GU04DFT2G 1 onsemi Unbuffered Single Inverter, SC-88A (SC-70-5 / SOT-353), 3000-REEL
$0.85 查看
DSC1001CL5-100.0000T 1 Microchip Technology Inc OSC MEMS 100.000MHZ CMOS SMD
暫無數(shù)據(jù) 查看
TLE6250GV33XUMA1 1 Infineon Technologies AG Interface Circuit, BICMOS, PDSO8, GREEN, PLASTIC, SOP-8

ECAD模型

下載ECAD模型
$1.22 查看

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

多年硬件從業(yè)經(jīng)驗,專注分享從研發(fā)到供應(yīng)鏈,再到精益制造過程中的經(jīng)驗和感悟!