[C 的那些眉角]malloc 之後一定要檢查 — 記憶體配置的防禦性寫法

平常在嵌入式系統上用 malloc
寫完之後覺得很爽,動態配置記憶體,好像很厲害。

uint8_t *buf = malloc(1024);
memset(buf, 0, 1024);
// 開始用 buf...

有一次朋友看了一眼問我:「malloc 失敗怎麼辦?」

我說:「會失敗嗎?記憶體應該夠吧?」

他說:「嵌入式的 heap 就那麼大,你確定嗎?」

我 ........ 當然不是很確定。

閱讀全文

[C 的那些眉角]指標用完要歸零 — 懸空指標的恐怖故事

有一種 bug,我只要想到就頭皮發麻。

明明程式跑得好好的,突然在某個完全不相關的地方 crash,
或是資料莫名其妙被改掉,
或是在開發機上完全正常,到了產品上偶爾出問題。

很多時候,追到最後都是同一個兇手:

懸空指標(Dangling Pointer)。

閱讀全文

[C 的那些眉角]函式介面設計 — 呼叫時不易搞錯

話說有一種 bug 很特別。

不是邏輯寫錯,不是演算法有問題,
而是呼叫端用錯了你的函式

參數順序傳反了、單位搞錯了、忘記先初始化就呼叫、
buffer 大小傳錯了……

這種 bug 有時候很難發現,因為程式可能還是跑起來,
只是結果不對,或是偶爾 crash。

我以前遇到這種情況,第一反應是「呼叫端的問題,他用錯了」。

但後來慢慢體會到:如果很多人都用錯,問題通常在介面設計,不在使用者。

閱讀全文

[C 的那些眉角]參數傳遞的眉角 — `const` 用對了嗎?

const 這個關鍵字,我用了很久都只會這樣寫:

const int MAX_SIZE = 256;

然後在函式參數上偶爾加一下,
覺得「這樣比較專業」,但其實不太確定為什麼要加。

直到有一次,同事在 code review 留言:

「這個參數應該加 const,你這樣寫呼叫端不知道你會不會改它。」

我才開始認真研究 const 在參數傳遞上到底是什麼意思。

結果發現,const 跟指標放在一起,
光是位置不同,意思就完全不一樣。

閱讀全文

[C 的那些眉角]回傳值不要亂丟 — 錯誤處理的設計

剛開始寫 C 的時候,我的錯誤處理大概是這樣:

void init_device(void) {
    i2c_init();
    sensor_init();
    uart_init();
    // 完成,應該沒問題吧?
}

回傳 void,裡面每個函式的回傳值都不管。

反正在開發板上跑都正常,就這樣出貨了。

然後客戶回報說裝置偶爾會初始化失敗,
但 log 完全看不出來哪個步驟出問題,
因為根本沒有任何錯誤處理。

那次之後,我開始認真思考:錯誤處理不是可選的,是必須的。


最常見的壞習慣

1. 完全不處理回傳值

閱讀全文