天天看點

【C語言進階剖析】21、宏定義與使用分析

1 定義宏常量

2 宏定義表達式

3 宏表達式與函數表達式的對比

3 宏有作用域嗎?

4 強大的内置宏

5 小結

c 語言中的宏定義

#define 是預處理器處理的單元實體之一

#define 定義的宏可以出現在程式的任意位置

#define 定義之後的代碼都可以使用這個宏

#define 定義的宏常量可以直接使用

#define 定義的宏常量本質為字面量

下面定義的宏正确嗎?

【C語言進階剖析】21、宏定義與使用分析

代碼如下:

先進行預編譯

預編譯通過,打開 21-1.i 檔案,如下所示:

宏定義被直接文本替換,我們定義的 #define 消失了,可以看出,p2,p3 顯然指向的字元串不符合 c 語言的文法結構,是錯誤的,預處理隻是進行文本替換,不負責類型檢查,真正查找錯誤還靠編譯階段。

小貼士:宏定義不占記憶體空間,因為宏在預處理階段就會被替換掉,到了編譯的階段是沒有宏存在的,自然到不了可執行檔案中,是以它不占記憶體空間。

#define 表達式的使用類似函數調用

#define 表達式可以比函數更強大,但是比函數更容易出錯

為什麼說宏很強大,宏可以做很多函數做不到的事,比如求解一個數組的長度;得出目前檔案名,目前行号;将參數類型作為參數傳入等等,這些函數都辦不到,後面我們一一說明

下面的宏表達式定義正确嗎?

【C語言進階剖析】21、宏定義與使用分析

先單步編譯,隻進行預處理,編譯通過;在進行編譯,編譯也通過,運作,結果如下:

上面的代碼可以看出,s2 是求 a 和 b 和的平方,結果是 5;m 是 a++ 和 b 的最小值,結果為 2,這和我們預想的結果不太對,為什麼會出現這種情況呢,我們來看看預處理之後的檔案(這裡為了省略不必要的資訊,注釋了 #include<stdio.h> 再執行的預處理)

可以看到, s2 = (a) + (b) * (a) + (b) = 1 + 2 * 1 + 2 = 5 m = ((a++) < (b) ? (a++) : (b)),執行 (a++) < (b) 結果為真,選擇(a++) ,此時 a 的值為 2,是以結果為 2 使用宏可以求出數組長度,這在 c 語言中用函數是一定辦不到的。

上面我們看到了宏表達式的副作用,在使用時一定要小心。

宏表達式被預處理器處理,編譯器不知道宏表達式的存在

宏表達式用 “實參” 完全替代形參,不進行任何運算(完全是文本替換)

宏表達式沒有任何的 “調用” 開銷

宏表達式中不能出現遞歸定義

比如下面的表達式就是不正确的,宏不能遞歸定義

【C語言進階剖析】21、宏定義與使用分析

有個有趣的問題:宏定義的常量或表達式是否有作用于限制?

看下面的程式合法嗎?在函數 def() 中定義了宏,能在函數 area() 中使用嗎?

【C語言進階剖析】21、宏定義與使用分析

上面的程式編譯運作通過,結果如下:

【C語言進階剖析】21、宏定義與使用分析

為什麼在函數 def() 中定義的宏能在函數 area() 中使用嗎?

#define 定義之後的代碼都可以使用這個宏,c 語言中作用域的概念的是針對變量和函數的,不針對宏,因為宏被預處理器處理,編譯器根本就不知道宏的存在,是以沒法将作用域的概念加到宏上面。

我們再來看一下單步編譯的結果

下面我們來看看 c 語言中強大的内置宏,可以檢視被編譯的檔案名,目前行号,被編譯的日期等等。

含義

執行個體

__file__

被編譯的檔案名

file.c

__line__

目前行号

25

__date__

編譯時的日期

nov 18 2019

__time__

編譯時的時間

21:01:01

__stdc__

編譯器是否遵循标準 c 規範

1

下面再來看一個示例:宏使用的綜合示例

【C語言進階剖析】21、宏定義與使用分析
malloc 的作用是在堆空間中申請一個數組,需要注意的是,這裡可以将參數類型作為自變量傳遞給宏 free 是用來釋放數組的。通過這個宏還可以在釋放空間的同時将指針置為空 foreach 宏也是宏比函數強大的地方,在函數中沒有辦法實作這個概念 主函數先申請一個數組,再将數組中的元素指派,最後将數組元素列印出來

預處理器直接對宏進行文本替換,參數不會進行求值、運算

預處理器不會對宏定義進行文法檢查,文法錯誤隻能被編譯器檢測

宏的效率高于函數調用,也會帶來一定的副作用