C語言的語法極其簡潔,即使是初次接觸程式語言的初學者也能很快學完它的語法。不過,C語言也是一門「靈活得過了頭」的程式語言,對於很多初學者來說,編寫C語言程序就好像拿著一堆最基本的磚塊,要修建一座大廈一樣,茫然找不到方向。
奇怪的C語言代碼
對於初學者來說,閱讀項目原始碼是學習和鞏固C語言編程能力的一個好方法——從前輩們的一些優秀C語言項目中,我們能夠學到很多編寫程序方面的思考方式,也就是一些程式設計師所謂的「編程思維」,看得多了,編寫C語言程序自然就手到擒來了。
不過雖然C語言的語法簡單,但是我們總會看到一些令人迷惑的代碼,例如下面這個函數,它來自某個開源項目,為了討論主題,我將一些不相關的細節略去了:
void *fun(void *ud, void *ptr, int size){
(void) ud;
(void) size;
// 其他代碼,未用到 ud 和 size 參數
...
}
fun() 函數中省略掉的代碼沒有使用到 ud 和 size 參數,這裡有兩個問題:一是既然用不到這兩個參數,為什麼不刪去它們呢?再就是兩個參數前的 (void) 類型轉換有什麼用呢?
首先考慮第一個問題
前文提到 fun() 函數來自一個C語言程序開源項目,該項目比較複雜,但是我們知道再複雜的程序項目也是一行一行代碼敲出來的,而且,在後續的開發中,可能會修改之前的設計。明白這一點,要回答第一個問題就簡單了。
可能在之前的設計中,fun() 函數是用到了 ud 和 size 參數的,只是後來的設計發現 fun() 函數不必使用這兩個參數,但是發現整個C語言項目由大量使用 fun() 函數的代碼。
如果刪去這兩個參數,那麼 fun() 函數的原型就改變了,開發人員將不得不逐個修改整個C語言項目中所有調用 fun() 函數的代碼,這樣的工作量巨大,極其容易給C語言項目引入 bug。因此,倒不如繼續保留 fun() 函數的原型不變了。
另外,讀者應該已經知道C語言是不支持重載的,因為如果該C語言項目需要使用 fun() 函數對接某些 API,那麼fun() 函數就必須符合 API 指定的原型,因此 fun() 函數中有未使用的參數其實是「身不由己」的。
還有一種情況,fun() 函數可能是某個「函數家族」裡的一個,該「函數家族」由一個統一的函數指針管理(為了方便,以及提高效率,實例可參考我之前文章。),因為「統一的函數指針」類型是固定的,所以 fun() 函數的原型必須符合該函數指針的原型,所以,即使 fun() 函數用不到 ud 和 size 參數,也是不能將其刪除的,否則就無法通過「統一的函數指針」調用 fun() 函數了。
當然了,也有可能純粹是因為開發人員懶得修改 fun() 函數原型。現在明白了第一個問題,再來考慮第二個問題。
為什麼要在未使用的參數前添加 (void) 呢?
在解答這個問題之前,我們先做一個實驗:編寫下面這段C語言代碼,也即刪去 (void)ud 和 (void)size:
void *fun(void *ud, void *ptr, int size){
// 其他代碼,未用到 ud 和 size 參數
...
}
在編譯這段C語言代碼時,編譯器常常會給出下面這樣的「參數未使用(unused parameter)」警告信息:
t.c: In function 『fun』:t.c:3:22: warning: unused parameter 『ud』 [-Wunused-parameter]
很多C語言程式設計師會忽略編譯器發出的警告信息,但這是非常不好的習慣,解決警告信息能夠幫助我們最大程度地避免最終C語言程序出現bug。要解決「參數未使用(unused parameter)」警告信息,最直接的方法就是使用它了:
void *fun(void *ud, void *ptr, int size){
ud;
// 其他代碼,未用到 ud 和 size 參數
...
}
但是編譯器又會給出「C語言語句無效」的警告信息:
t.c:5:5: warning: statement with no effect [-Wunused-value]
ud; ^~
為了避免出現這樣的警告信息,我們當然可以對 size 和 ud 參數做一些其他操作,例如:
void *fun(void *ud, void *ptr, int size){
ud = (void *)size;
// 其他代碼,未用到 ud 和 size 參數
...
}
可是這樣雖然能夠避免C語言編譯器發出警告,但是這樣會讓其他閱讀代碼的程式設計師費解:「NND,ud = (void *)size;這句到底什麼意思呢?」
因此,避免編譯器發出參數未使用的警告信息,最好不要像上面這樣操作,採用 (void) 操作更好:
void *fun(void *ud, void *ptr, int size){
(void) ud; (void) size;
// 其他代碼,未用到 ud 和 size 參數
...
}
C語言程式設計師都知道 void 表示空,因此(void)ud 和(void)size 顯然表示不關心 ud 和 size 的操作。這樣一來,我們的意圖一眼就能看出,而且還能避免編譯器發出警告信息。
小結
在C語言程序開發中,定義函數時,有時會不可避免的定義一些使用不到的參數,這時編譯程序時,編譯器往往會發出警告信息。C語言程式設計師不應該忽視每一個警告信息,因此可以藉助 (void)操作屏蔽掉這樣的警告信息,以免更重要的編譯器警告被淹沒在信息流裡。
歡迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。
未經許可,禁止轉載。