每個代碼工程都在使用的宏定義#define 你真的明白了嗎

1、前言

編譯一個C程序的第一個步驟被稱為預處理,也就是在源代碼編譯之前對其進行一些文本替換的操作,而一個常見的約定是把宏名字全部大寫(用以區分宏和函數,因為在某些場合我們會以宏的形式來實現函數的功能)。

2、宏的常用場合

為了便於一些通用代碼模板的維護與擴展,我們在實際開發中會經常使用宏定義。使用#define指令,你可以把任何文本替換到程序中。這裡有幾個例子:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎

第1個定義只是為關鍵字register創建了一個簡短的別名。第2個定義用一個更具描述性的符號來代替一種用於實現無限循環的for語句類型。最後一個#define定義了一種簡短記法,以便在switch語句中使用。它自動的把一個break放在每個case之前,原因在於在switch語句中,不遇到break就不會主動退出,一直到語句執行完畢,而很多人常常會在case分支忘記加上break。值得注意的是,如果定義中的語句非常長,它可以分成幾行,除了最後一行之外,每行的末尾都要加上一個反斜槓,如下所示:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎

3、宏與函數

宏非常頻繁的用於執行簡單的計算,比如在兩個表達式中尋找較大(或較小)的一個:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎

為什麼不用函數來完成這個任務呢?有兩個原因。首先,用於調用和從函數返回的代碼很可能比實際執行這個小型計算工作的代碼更大,所以使用宏比使用函數在程序的規模和速度方面都更勝一籌。但是,更為重要的是,函數的參數必須聲明為一種特定的數據類型,所以它只能在類型合適的表達式上使用。反之,上面這個宏可以用於整型、長整型,浮點型以及其他任何可以用‘>’操作符比較大小的類型。也就是說,宏與類型無關。和函數相比的話,宏也是有缺點的,不利之處就在於每次使用宏時,一份宏定義的代碼都將重新拷貝並插入程序中,除非宏非常短,否則使用宏可能會大幅度增加程序的長度。

正如上面所說,宏與函數不同,宏在連續幾個調用中所接收的參數類型可以不同,只是簡單的對字符串進行替換而已。特別需要注意的一點是:在宏的擴展中,空格會對擴展的結果造成很大的影響,示例如下:

每個代碼工程都在使用的宏定義#define 你真的明白了嗎

被擴展為:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎


而:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎

則被擴展為:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎

造成的結果是:他們所表示的意思天差地別(沒有看明白的小夥伴歡迎在評論區交流哦)。

另外給大家補充一個小技巧,平常工作的開發中不一定用得到,理解這個思想就行。如下:Steve Bourne在編寫UNIX第七版的shell(命令解釋器)時,採用了C預處理器(也就是宏#define)使C語言看上去更像是shell編程裡的風格,shell編程代碼中有顯示的“結束語句”提示,諸如if...fi或者case...esac等,調試起來會更容易。Steve認為僅僅一個“}”是不夠的,因此他建立了一批宏定義如下:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎

這樣,就可以像下面這樣編寫代碼:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎

再看一下相應的C代碼:


每個代碼工程都在使用的宏定義#define 你真的明白了嗎

本文章結束之前提一個小問題:上圖中的C代碼是否有錯呢?如果有錯的話錯在哪?沒錯的話為什麼在函數名和函數體之間有奇怪的s1和s2的聲明?歡迎在評論區交流。

請關注筆者,後續會定期更新C編程相關知識和技巧,我們一起學習一起進步。


分享到:


相關文章: