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

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

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

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

布置場景

因為我的遙控器是基于無線RF通信的,因此我需要定義了兩個幀結構,分別用數(shù)組來存放,然后再發(fā)送和接收函數(shù)中直接處理數(shù)組。幀結構的前四個字節(jié)作為通信的地址,也就是一個過濾ID,不符合這個ID的幀直接放棄掉。為了讓系統(tǒng)中兼容更多的設備對,我把這個通信地址定義為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ù)中處理這個接收到的幀,第一件事情肯定是比對地址是否匹配,假設我宏定義的地址為:

#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ù)組或者結構體,總之,它不是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));

第三種,如果你還是糾結于優(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){    // 我還得死?。。。?!}

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風險等級 參考價格 更多信息
TJA1051T/3/2Z 1 NXP Semiconductors Interface Circuit
$9.33 查看
CY62167EV30LL-45ZXI 1 Cypress Semiconductor Standard SRAM, 1MX16, 45ns, CMOS, PDSO48, TSOP1-48
$69.33 查看
553SCMGI 1 Integrated Device Technology Inc COL-8, Cut Tape

ECAD模型

下載ECAD模型
$8.47 查看

相關推薦

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