奇怪的C語言代碼,在變量前加上(void)是什麼操作?有什麼用?

2020-12-25 IT劉小虎

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等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。

未經許可,禁止轉載。

相關焦點

  • C語言全局變量那些事兒
    由於C語言每個源文件單獨編譯,所以t.h分別包含了兩次,所以int a就被定義了兩次。兩個源文件裡變量b和函數指針變量main被重複定義了,實際上可以看做代碼段的地址。這裡有個「強符號(strong)」和「弱符號(weak)」的概念——前者指的是定義並且初始化了的變量,比如foo.c裡的結構體b,後者指的是未定義或者定義但未初始化的變量,比如main.c裡的整型b和c,還有兩個源文件都包含頭文件裡的a。
  • 堅果內核變量居然使用拼音命名!還拼錯了!如何規範C語言編程?
    ,錘子科技貼吧名為jocover的網友吐槽說,代碼中變量居然用拼音命名!說明:清晰準確的函數、變量等的命名,可增加代碼可讀性,並減少不必要的注釋。2-13:在代碼的功能、意圖層次上進行注釋,提供有用、額外的信息。
  • C語言代碼中 extern "C"的前世今生
    ,你應該還會想:「嗯⋯是啊,我們的代碼都是這樣寫的,從來沒有因此碰到過什麼麻煩啊~」。這與C++有什麼關係呢? 看看__cplusplus(注意前面是兩個下劃線) 的名字你就應該知道它與C++有很大關係。__cplusplus是一個C++規範規定的預定義宏。你可以信任的是:所有的現代C++編譯器都預先定義了它;而所有C語言編譯器則不會。另外,按照規範__cplusplus的值應該等於1 9 9 7 1 1 L ,然而不是所有的編譯器都照此實現,比如g++編譯器就將它的值定義為1。
  • 堅果內核變量居然使用拼音命名!還拼錯了!如何規範C語言編程?
    ,錘子科技貼吧名為jocover的網友吐槽說,代碼中變量居然用拼音命名!說明:清晰準確的函數、變量等的命名,可增加代碼可讀性,並減少不必要的注釋。2-13:在代碼的功能、意圖層次上進行注釋,提供有用、額外的信息。
  • C語言程序開發中常用的assert,到底有什麼用,有哪些好處?
    相信不少初學者在閱讀一些開源的C語言項目時,都會看到 assert 關鍵字,那麼它有什麼用呢,又有哪些好處呢?可以根據錯誤信息排查代碼編譯並執行這段C語言代碼,得到如下輸出:# gcc t.c與此同時,後續的C語言代碼將不再有執行機會。其他C語言中的 assert() 還有個方便點在於,程式設計師一旦開發程序完畢,可以在 release 版本程序中關閉 assert() 宏,此時 assert() 宏將不再提供功能,它在程序中的作用等同於一個空格。
  • PIC單片機C語言編程教程(1)
    針對 PIC 單片機的軟體開發,同樣可以用 C 語言實現。但在單片機上用 C 語言寫程序和在 PC 機上寫程序絕對不能簡單等同。現在的 PC 機資源十分豐富,運算能力強大,因此程式設計師在寫 PC 機的應用程式時幾乎不用關心編譯後的可執行代碼在運行過程中需要佔用多少系統資源,也基本不用擔心運行效率有多高。
  • C語言陷阱與技巧第28節,模擬「面向對象」編程,怎樣定義私有成員
    上一節討論了結合指針和結構體語法,C語言也能實現「面向對象」編程。由此可以看出C語言是一門極其靈活的語言,簡潔的語法即可實現複雜的程序。C語言「對象」的成員變量不過,在面向對象編程中,對象不僅僅有成員函數,也應該有成員變量。成員變量允許每一個對象都有獨立存放數據的能力,各個對象的數據互不幹擾。
  • C語言中的extern "C"
    這與C++有什麼關係呢? 看看__cplusplus(注意前面是兩個下劃線) 的名字你就應該知道它與C++有很大關係。__cplusplus是一個C++規範規定的預定義宏。你可以信任的是:所有的現代C++編譯器都預先定義了它;而所有C語言編譯器則不會。另外,按照規範__cplusplus的值應該等於1 9 9 7 1 1 L ,然而不是所有的編譯器都照此實現,比如g++編譯器就將它的值定義為1。
  • C與C+的互相調用!就像大學宿舍一樣,我用你的,你用我的!
    因為項目本身是通過純c語言編寫,而gtest則是一個c++編寫的測試框架,其中必然涉及c與c++之間的相互調用。 注意,本文的前提是,c代碼採用gcc等c語言編譯器編譯c代碼,採用g++等c++編譯器編譯c++代碼,如果c和c++代碼統一使用g++編譯,大部分情況是可以實現兩者代碼相互調用的。
  • 【愛找茬】都是C語言,單片機C語言和普通的C語言究竟有什麼差異呢?
    許多小夥伴在學完C語言後想入門單片機,但學著學著發現明明都是C語言,為什麼單片機C語言和我當初學的C語言有差異呢?今天小編就來梳理我們平時所學的C語言與單片機C語言的有什麼樣的不同。
  • 乾貨|C語言文件的基本操作!
    本文轉載自【微信公眾號:手機電腦雙黑客,ID:heikestudio】經微信公眾號授權轉載,如需轉載與原文作者聯繫c語言對文件的操作主要分為:按字符操作,按行操作,按內存塊操作主要的函數:fopen():FILE * fopen(_In_z_ const char * _Filename, _In_z_ const char
  • 如何用C語言實現面向對象編程OOP?
    如何用C語言實現面向對象編程OOP? C語言的特性,實現OOPC是一門面向過程的語言,但它依舊可以實現大多數面向對象所能完成的工作。比如面向對象的三大特性:封裝、繼承、多態。我們以下圖來寫代碼舉例子。
  • PIC單片機asm與C混合編程
    四、深入探討PICC之位操作1、用位操作來做一些標誌位,也就是BOOL變量.可以簡單如下定義:bit a,b,c;PICC會自動安排一個內存,並在此內存中自動安排一位來對應a,b,c.由於我們只是用它們來簡單的表示一些0,1信息,所以我們不需要詳細的知道它們的地址\位究竟是多少,只管拿來就用好了。
  • 學校裡學不到的C語言教程之4:奇怪的第一行代碼與重複的函數名
    時間久了我們一定會想知道,到底那些 h 文件裡有什麼東西可以讓我們完成這些功能呢?當那些有心的同學找到 winsock.h 的代碼時一定會看到裡面只是些函數的聲明,最多再加點結構體的聲明。直接把函數體寫在 h 文件裡的可能性非常之小(除非是性能需要的 inline 函數)。
  • C語言中變量的命名規則是什麼?
    在C語言中,變量的命名是有明確規則的:1、只能由字母、數字、下劃線組成;2、第一個字符必須是英文字母;3、有效長度為255個字符;、 continue、 for、 signed 、void、default 、goto、 sizeof 、volatile、 do 、if 、while、 static。
  • 快速上手系列-C語言之基礎篇(一)
    按照之前的計劃,這篇文章本該寫C語言簡介,包括一些歷史背景,發展狀況, 語言特點什麼的,這些東西著實比較囉嗦。吃飯用的筷子,到現在我都不知道它的由來,不知道它是在什麼歷史背景下誕生的有些東西還是有必要了解一下,比如,C語言所產生的代碼運行速度比彙編語言編寫的代碼運行速度稍慢一點,但還是有得一拼,同時C語言要比彙編語言編寫的代碼可讀性高,可移植性好。
  • 【愛找茬】都是C語言,單片機C語言和普通的C語言究竟有什麼差異呢?
    許多小夥伴在學完C語言後想入門單片機,但學著學著發現明明都是C語言,為什麼單片機C語言和我當初學的C語言有差異呢?今天小編就來梳理我們平時所學的C語言與單片機C語言的有什麼樣的不同。
  • 「C語言從入門到入土」必備C語言基礎筆記整理
    一、C語言1、什麼是C語言?C語言是人寫機器看的一種語言。C語言是高級語言中的低級語言。C語言貼近硬體。C語言的入門學習比較簡單。彙編語言——>B語言——>C語言2、C語言的特性首先C語言就是你的女朋友。無論你讓它幹什麼,它絕對不會自己找到方法。
  • C語言中的字符串操作函數
    我們知道,c/c++之所以使用起來靈活,很大原因歸因於它能夠它對能夠對內存的直接操作,所以本文我主要講述一下c中的字符串操作函數。那麼回頭來看第一行代碼,也就是指針常量:我們跳過變量類型 int ,那麼const修飾的是*,所以它指向的值不能修改第二行代碼,常量指針:同樣,我們跳過int,發現const是直接修飾的p,所以它的指向不能改變。兩者有細微的差別,請大家注意。
  • C語言中,全局變量濫用的後果竟如此嚴重?
    01啥是全局變量說起全局變量,就不得不提到「全局變量,局部變量,靜態全局變量,靜態局部變量」,這些都是程式語言中的基本概念。此現象在早期彙編轉型過來的程式設計師以及初學者中常見,這幫傢伙幾乎把全局變量當作函數形參來用。在.h文檔裡面定義許多雜亂的結構體,extern一堆令人頭皮發麻的全局變量,然後再這個模塊裡邊賦值123,那個模塊裡邊判斷123分支決定做什麼。