c語言 巨集中 的用法(續)

2021-07-03 23:47:45 字數 2692 閱讀 2088

如下**,不明覺厲。

#   define ext2_debug(f, a...)  

特別是其中printf(f, ## a);這樣的用法。

google之後在stackoverflow找到了這個問題以及解答。

詳細的解答跳轉到了gnu的gcc文件:

譯文如下:

3.6 variadic macros (可變引數的巨集)

乙個巨集可以像乙個函式一樣,接收數量可變的引數。這樣的語法在巨集中的定義與在函式中的定義是類似的。

這裡是乙個例子。

#define eprintf(...) fprintf (stderr, __va_args__)

這種巨集被稱為

variadic.。當這種巨集被呼叫時,引數列表中在最後乙個被命名引數之後的所有字段,包括所有逗號,都變為可變的引數(

variable argument

)。識別符號

__va_args__

無論出現在巨集的什麼位置,都將被這一系列的字段替換掉。

因此,我們可以得到這樣的表示式。

eprintf ("%s:%d: ", input_file, lineno)

==>  fprintf (stderr, "%s:%d: ", input_file, lineno)

可變引數在插入到巨集的表示式之前,是乙個完整的巨集的擴充套件,就像普通的引數一樣。你可以使用#將操作符轉化為字串,也可以使用##將多個字段連線為乙個字段。(但是,如下將展示##的乙個重要的特別用法。)

如果你的巨集是複雜的,對於可變的引數,你應該想要乙個比

__va_args__

更具有表達性的名字。作為乙個表達方式,cpp允許這一點。你可以在「...」之前寫乙個引數的名字,用於表達這個引數是可變引數。

之前的eprintf巨集可以使用這樣的表達方式被寫為:

#define

eprintf (args...) fprintf (stderr, args)

注意,不可以在同乙個巨集中同時使用這種表達方式和

__va_args__。

你也可以使用

__va_args__

在乙個可變引數的巨集中

來表達可變的引數。

我們將像這樣來重新定義eprintf。

#define eprintf(format, ...) fprintf (stderr, format, __va_args__)

這樣的方式看起來更加具有可讀性,但是這樣的方式缺乏適應性:在格式化的引數之後,我們必須提供至少乙個引數。在標準c中,你不能在命名引數和可變引數之間省略逗號。而且,如果你保留可變引數為空,你將得到乙個編譯錯誤,因為在格式化引數的後面將多出乙個逗號。

eprintf("success!\n", );

==> fprintf(stderr, "success!\n", );

gnu cpp提供兩種表示式,可以處理這個問題。

第一種,你將被允許不新增整個可變的引數

eprintf("success!\n" );

==> fprintf(stderr, "success!\n", );

第二種,字段連線操作符##被放在乙個逗號和乙個可變的引數之間時,具有一種特殊的意義。

如果你寫了如下表示式:

#define eprintf(format, ...) fprintf (stderr, format, ##__va_args__)

當eprintf這個巨集被呼叫時,可變的引數被省略,然後##之前的逗號被刪除。如果輸入乙個空的引數,這個過程將不會發生;如果##之前的字段不是逗號,這個過程也不會發生。

eprintf("success!\n");

==> fprintf(stderr, "success!\n");

====(未完待續,睡覺先)====

*****===接上回分解*****==

以上的解釋是針對一種情況,唯一的巨集引數為乙個可變變數的引數。這樣的解釋是含糊不清的。而嘗試區分沒有引數的情況是否是乙個空引數或者乙個錯誤引數是沒有意義的。在這種情況下,c99標準清楚的表明逗號必須保留,然而現存gcc擴充套件版習慣於吞掉逗號。所以當cpp依照c99標準時,cpp保留逗號 ,而其他的則刪除逗號。

c99提出來唯一的一處要求:識別符號__va_args__能夠出現在乙個可變變數巨集的替換列表裡。但是這個識別符號不可以作為乙個巨集的名字,巨集的引數名,或者在乙個不同型別的巨集中。這個表示也禁止在open text中使用;標準是不明確的。我們推薦避免它的其他用法,除了它被定義用法。(open text是什麼,我也沒明白,google也沒找到合理解釋。)

可變變數的巨集是乙個c99中的新功能。gnu cpp 已經支援這個功能很長時間,但是只支援一鐘可變引數的命名(支援『args...』;不支援'...' 和 __va_args__)。如果你考慮對gcc之前版本的相容性,那麼你應當只使用被命名的可變變數'args...'。另一方面,如果你考慮依照c99的實現,你應當只使用__va_args__。

之前版本的cpp實現了更加通用的針對逗號刪除的擴充套件。在此份文件中,我們限制了這一點,為了使得與c99的差異最小。為了在此版gcc和之前的gcc版本中獲得同樣的作用,『##』之前的字段必須是乙個逗號,並且在逗號和逗號之前的任何字段之間必須有乙個空格:

#define eprintf(format, args...) fprintf (stderr, format , ##args)

*****=翻譯部分完結*****=

c的標準之間,也是略有差異的。有一些特別的寫法,可能是某個標準的特殊用法。

C 中的巨集(續)

1.1.1.運算子 字元在 define巨集定義中是乙個將引數名轉換為字串的運算子,放在巨集引數的前面。例如 include include define declare non copiable begin cname class cname public virtual std string t...

C語言巨集中的 和 的用法

與 在巨集定義中的 巨集展開 include define f a,b a b define g a a define h a g a int main 巨集展開時 如果巨集定義以 開頭,不展開引數,直接替換。故g f 1,2 f 1,2 f 1,2 如果巨集定義不以 開頭,展開引數,直接替換,由外...

C 巨集中 的用法

c語言中的巨集是乙個很簡單粗暴的設計,主要功能就是replace。為了更方便地替換,引入了巨集函式這一概念。巨集函式用引數替換預先定義的識別符號在巨集定義中的每一次出現。配合 和 可以用巨集簡單高效地完成一些複雜的操作。稱之為字串化操作符 stringizing operator 它將函式巨集的實際...