在函式式巨集定義中,#
運算子用於建立字串,#
運算子後面應該跟乙個形參(中間可以有空格或tab),例如:
#define str(s) # s用str(hello world)
cpp
命令預處理之後是"hello␣world"
,自動用"
號把實參括起來成為乙個字串,並且實參中的連續多個空白字元被替換成乙個空格。
在巨集定義中可以用##
運算子把前後兩個預處理token連線成乙個預處理token,和#
運算子不同,##
運算子不僅限於函式式巨集定義,變數式巨集定義也可以用。例如:
#define concat(a, b) a##b預處理之後是concat(con, cat)
concat
。再比如,要定義乙個巨集展開成兩個#
號,可以這樣定義:
#define hash_hash # ## #中間的
##
是運算子,巨集展開時前後兩個#
號被這個運算子連線在一起。注意中間的兩個空格是不可少的,如果寫成####
,會被劃分成##
和##
兩個token,而根據定義##
運算子用於連線前後兩個預處理token,不能出現在巨集定義的開頭或末尾,所以會報錯。
我們知道printf
函式帶有可變引數,函式式巨集定義也可以帶可變引數,同樣是在引數列表中用...
表示可變引數。例如:
#define showlist(...) printf(#__va_args__)預處理之後變成:#define report(test, ...) ((test)?printf(#test):\
printf(__va_args__))
showlist(the first, second, and third items.);
report(x>y, "x is %d but y is %d", x, y);
printf("the first, second, and third items.");在巨集定義中,可變引數的部分用((x>y)?printf("x>y"): printf("x is %d but y is %d", x, y));
__va_args__
表示,實參中對應...
的幾個引數可以看成乙個引數替換到巨集定義中__va_args__
所在的地方。
呼叫函式式巨集定義允許傳空引數,這一點和函式呼叫不同,通過下面幾個例子理解空引數的用法。
#define foo() foo預處理之後變成foo()
foo
。foo
在定義時不帶引數,在呼叫時也不允許傳引數給它。
#define foo(a) foo##a預處理之後變成:foo(bar)
foo()
foobarfoo
foo
在定義時帶乙個引數,在呼叫時必須傳乙個引數給它,如果不傳引數則表示傳了乙個空引數。
#define foo(a, b, c) a##b##c預處理之後變成:foo(1,2,3)
foo(1,2,)
foo(1,,3)
foo(,,3)
1231213
3
foo
在定義時帶三個引數,在呼叫時也必須傳三個引數給它,空引數的位置可以空著,但必須給夠三個引數,foo(1,2)
這樣的呼叫是錯誤的。
#define foo(a, ...) a##__va_args__預處理之後變成:foo(1)
foo(1,2,3,)
112,3,
foo(1)
這個呼叫相當於可變引數部分傳了乙個空引數,foo(1,2,3,)
這個呼叫相當於可變引數部分傳了三個引數,第三個是空引數。
gcc
有一種擴充套件語法,如果##
運算子用在__va_args__
前面,除了起連線作用之外還有特殊的含義,例如核心**net/netfilter/nf_conntrack_proto_sctp.c
中的:
#define debugp(format, ...) printk(format, ## __va_args__)
printk
這個核心函式相當於printf
,也帶有格式化字串和可變引數,由於核心不能呼叫libc
的函式,所以另外實現了乙個列印函式。這個函式式巨集定義可以這樣呼叫:debugp("info no. %d", 1)
。也可以這樣呼叫:debugp("info")
。後者相當於可變引數部分傳了乙個空引數,但展開後並不是printk("info",)
,而是printk("info")
,當__va_args__
是空引數時,##
運算子把它前面的,
號「吃」掉了。
原址:
c語言中的預處理(1)
剛接觸c語言的人都知道,每次主程式開始前都得寫乙個 include。但是卻不知道為什麼非要加這個東西,學了一段時間後,似乎會明白一點,但還是稀里糊塗的,在這裡,我們就把這玩意兒抽絲剝繭弄個明白。在c語言裡,有一種非常有用而又必不可少的部分,叫做預處理,說道這兒有人就笑了,不就是 include和 d...
C語言中的預處理命令
預處理指令 1.在源程式編譯之前,先進行一些特殊的預處理指令作解釋,產生乙個新的源程式 這個過程稱為編譯預處理 之後在進行通常的編譯。2.為了區別預處理指令和一般的c語言,所有預處理指令都是以 開頭,並且結尾無分號 3.預處理指令可以出現在程式的任何位置,它的作用範圍是從它出現的位置到檔案尾。4.c...
C 語言中的預處理命令
由於預處理命令不是 c 語言本身的組成部分,所以 c 編譯程式不能識別它們,也就不能直接對它們進行編譯,所以在 c 程式編譯之前,必須先對預處理命令進行處理,處理後程式中不再包括預處理命令了,再由編譯程式處理得到目標 c 語言提供的預處理命令共有三中 巨集定義,檔案包含和條件編譯,預處理命令以 開頭...