巨集 macro 定義與使用

2021-09-26 09:35:32 字數 3805 閱讀 4090

巨集的定義

巨集的替換與**展開

巨集的替換產生的問題

獲取巨集引數名稱

巨集引數的結合

巨集的取消

巨集定義的換行連線

標準預處理巨集

巨集(macro)是基於#define所實現的另一種預處理功能。

與基本的#define定義的是常量相比,巨集(macro)允許多個引數化替換,引數中可以是固定的字串,也被一些變數所替代。這個替換的操作將在預編譯的時候完成。

巨集(macro)的作用是用比較簡單的方式表示複雜的函式呼叫,以提高程式的可讀性。

巨集的定義

巨集的定義遵循以下格式:

#define macro_name(list_of_var) substitution_string

macro_name()括號中支援0個到多個引數,引數間用,分隔開;定義的最後一部分是巨集的表示式,在實際的預處理中會將該表示式替換到**正文中去,這裡要特別注意的是替換這個詞(坑)。

下面是乙個巨集的定義示例,用於求出兩個數的乘積:

#define multi(x, y) x * y;

接收引數的巨集看起來就像是函式。

巨集的替換與**展開

編譯器在預編譯時會把用到巨集的**替換成巨集定義的表示式。

如上面定義的巨集multi,可以如下**中使用:

**檔案: macro.c

int multi = multi(2, 3);

在預編譯時,替換操作的檢視可以通過編譯器命令檢視效果,命令如下:

gcc -e macro.c > result.txt

// 或者 使用 -p 引數可以過濾到一些編譯器鏈結標記符號資訊

gcc -e -p macro.c > result.txt

開啟result.txt則會看到巨集的替換後**展開的效果為:

int multi = 2 * 3;; // 確實是兩個";" 純替換效果

最後根據表示式的運算,multi的值為6。

巨集的 替換 產生的問題

如上所示,巨集 替換 操作是純表示式的原文替換,連末尾的「;」都不會落下。那麼,這在某此情況下會產生一些不符合預期的結果。

繼續以巨集multi為例,如果改一下引數如下:

int multi = multi(2, 3 + 2);

以上的**我們希望得到的結果是10,但**輸出卻是8。我們使用預編譯命令將預處理過巨集的**展開檢視如下:

int multi = 2 * 3 + 2;;

可見,由於預處理時,對巨集的操作只是替換,經替換後的**由於運算子的優先順序問題,結果可能與預期的是不一致的。在這個例子中,會先執行乘法得到6後再加上2結果為8。以上的這種問題還會發生在」++」、」- -「運算上。

所以在有引數的巨集中,為了使用運算與預期的一致,需要對引數增加左右括號以保證運算結果,並且取消在巨集定義處」;」的結尾。定義寫法如下:

#define multi(x, y) (x)*(y)

獲取巨集引數名稱

在巨集的表示式中,配合」#」的使用可以獲取巨集引數的名稱。

如下定義了乙個列印字串的例子:

#define printstr(var) printf(「var=%s」, var)

在上面的表示式中,只能固定的列印出引數名稱var=,但在專案中引數var的名稱和其值一樣是不固定的,有時候需要能夠知道該var的命名,則可以這麼做:

#define printstr(var) printf(#var"=%s" , var)

則可以獲取當前的字串名稱的命名。運用如下:

#define printstr(var) printf(#var"=%s" , var)

struct student;

int main (int argc, char ** ar**) ;

printstr(mine.name);

printf("\n\n");

printstr(「break.line\n」);

return 0;

}則列印輸出如下:

printstr

printstr

再使用gcc命令把巨集展開後的**貼出來看一下:

struct student;

int main (int argc, char ** ar**) ;

printf(「mine.name」"=%s" , mine.name);

printf("\n\n");

printf("「break.line\n」""=%s" , 「break.line\n」);

return 0;

}可以看出,在巨集的表示式中#var的結果**在預編譯的時候會把引數的原文一字不動的替換到**中。這樣來獲取引數的命名。

巨集引數的結合

巨集的表示式中還支援對巨集引數進行拼接,定義如下:

#define macro_name(var1, var2, var3) var1##var2##var3

在上面的例子中,對巨集的三個引數進行了拼接,拼接規則是在巨集的表示式中,引數與引數之間用##連線起來,中間不能有空格。這個功能可以用於合成變數名稱,或是從兩個或多個巨集引數中生成乙個格式控制字串。

用法示例:

#define printstr(var) printf(#var"=%s", var)

#define join(va, vb, vc) va##vb##vc

int main (int argc, char ** ar**)

使用gcc命令對巨集進行**展開後的預處理**為:

int main (int argc, char ** ar**)

所以如上**執行結果為:

join(name, car, 1)=fit

巨集的引數結合功能可以通過替換功能組合成乙個新的引數或變數名稱,但值得注意的是這些操作都是在編譯的預處理過程中實現的,即**執行之前就已經決定了新的引數或變數的最終形態,無法在**的真實執行過程中再加工。

巨集的取消

定義了乙個巨集後,要對巨集進行取消,需要使用指令:

#undef macro_name

通常#undef會和#ifdef或#ifndef等指令配合使用。

#define multi(x, y)

x * y

使用\進行換行連線,這樣巨集定義指令會在下一行的第乙個非空白字元處繼續。需要注意的是\必須是當前行的最後乙個字元,其後必須是回車。

標準預處理巨集

c/c++的編譯器也支援了大量的標準預處理巨集。這裡只說下常用的幾個,更多的詳情資料3.7. predefined macros。

需要注意的是不同的編譯器相同功能的巨集名稱可能命名不一樣。

file

把當前**原始檔的絕對路徑表示為乙個字串常量。

function

也被命名為__func__,用於該巨集所在函式體的函式名稱。可以方便除錯或異常判斷。

line

得到當前巨集所在的**的行數,是乙個int型。一般和__functin__中配合來標識源**中的什麼地方發生了某個事件。

date

生成日期的字串表示式,格式為mmm dd yyyy,其中mmm是月份如」jan」、」feb」等等。

time

提供了包含時間值的字串,格式是hh:mm:ss。再次強制巨集只是在預處理的時間才進行的**替換,所以這裡的時間指的是編譯器的執行時間點,而非程式執行時間。一般用這個巨集來記錄編譯器的編譯時間點。

**示例:

int main (int argc, char ** ar**)

巨集展開後的**為:

int main (int argc, char ** ar**)

巨集 macro 定義的簡介

1.1巨集的簡單使用說明 define square x x x 下面我們就來用一下這個巨集 printf result d n square 5 無可厚非,結果就是5 5 25 int a 5 printf result1 d n square a 1 第一眼可能會脫口而出36,但是你文字替換一下...

gdb 除錯巨集定義macro

c語言中的巨集定義,有著各種好處,當然也有它不好的一面。它實現的三個主要功能是 1 字串替換 2 標頭檔案包含 3 通用 模組的擴充套件 在 除錯時候,除錯巨集定義是比較麻煩的一件事,下面就介紹一種比較簡單的方法。include define max a,b a b a b define min a...

SV 巨集定義(macro)引發的災難

在開發中,用巨集定義可以帶來很多方便,但是使用不當,也會帶來災難,並且不容易debug出來,如下圖所示的程式碼 define switch 64 128 bit 0 define svt axi max data widtn d64 switch 64 128 bit d64 int lane nu...