寫 C 程式時偶而會遇到函數的原型宣告多設了一些暫時沒有用到的參數, 一般狀況下只要修改一下標頭檔中的宣告以及實際 C 函數的定義就好了. 不過偶而也會遇到不能改的情形, 例如:
- 套用某些框架系統時, 函數的原型宣告是框架系統提供的. (一改大概框架系統就不用升版了)
- 使用函數庫中某個函數的 call back, 而函數庫改不動, 或者沒原始程式, 沒得改.
- 在開發 embedded system 時, 我們需要實作或者改寫一些函數以取代 C 編譯器的一些預設行為, 例如: Keil ARM/MDK 中要把 printf() 改輸出到 SoC 的 UART.
下面一些方法可以在無法修改函數的原型宣告時, 把 C 編譯器對未使用的函數參數之告警消除.
- 方法一: 大部份的 C 編譯器應該都可以適用.
- 方法二: 方法一不行時先試這個. (Keil C51 適用)
- 方法三: 再不行, 改用這個.
- 方法四: GNU C/C++ 或 GNU 相容的 C 編譯器適用. 直接在未用的參數後面加上 __attribute__((unused)).
#define UNUSED(x) ( (void)(x) )
void f(int x, int y) {
UNUSED(x);
...
}
#define UNUSED(x) if(x){}
void f(int x, int y) {
UNUSED(x);
...
}
#define UNUSED(x) ( *(volatile typeof(x) *)&(x) = (x); )
void f(int x, int y) {
UNUSED(x);
...
}
void f(int x __attribute__((unused)), int y)
{
}
#define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
void f(int UNUSED(x), int y)
{
}
備註: 仔細比對這個 macro 所產生的轉換結果和原本的寫法, 其實有二個差異:
- __attribute__ 後面括號中的 unused 變成了 __unused__
- 參數 x 變成 UNUSED_x
第一點是一個防呆機制, 以防 user 其他的 header file 中有定義小寫的 unused 巨集. 這個現象在以前 unused 未標準化之前很常見, 現在的 GNU 雖然文件上只有 unused, 但是 __unused__ 依舊是認得的. 參考: https://stackoverflow.com/a/19892419
第二點其實也是個防呆機制: 如果一開始的 function 實作參數 x 真的是一個未用到的參數. 可是後來的版本, 你發現必需要使用它, 於是很直覺的就用了 (經常是從別的地方 copy paste 一小段程式碼過來), 然後就忘了把上面的 UNUSED 取消掉, 這樣 compiler 很清楚的會告訴你一個錯誤: x 是未定義的變數. 所以你會記得要把參數裡面的 UNUSED 拿掉; 而不是多產生了一個你可能視而不見的告警.
附記
為什麼一定要把編譯時的告警消除:
因為我吃過它的大虧, 當年我們家的專案整份 code 編譯下來一共 500 多個告警, 平常都相安無事, 反正告警就只是告警, 又不會死人, 所以沒有人想去動它 (萬一改壞了怎麼辦?). 直到有一天我修了一個功能上的 Bug, 結果機器一下子就當! (怎麼會!?? 心裡吶喊著...)
想當然大家都說是我改出問題來了, 可是我還滿確信的, 不是我改的部份有問題. 在想不出其他方法的情形下只好在 500 多個告警中找答案, 但是到底是哪些個不應該出現的告警有問題呢? 只有硬著頭皮一個一個看, 看懂了上網找答案看如何解決. 就這樣花了二天多才找到問題. 最後總共花了將近四天才把所有解得開的告警改完, 但還是剩下 8 個解不開. 不過狀況已經改善許多, 8 個告警多出一兩個你馬上有警覺, 要查也容易; 但是 500 個多出一兩個你跟本沒感覺, 要找到多出來的那一兩個也像是大海撈針.
純粹個人經驗, 提供給大家參考.
留言列表