2010-11-12 134 views
13

下面是一個相關示例。這顯然是無效的C,但我只是在這裏處理預處理器,所以代碼實際上並不需要編譯。在C宏擴展期間,是否有擴展爲「/ *」的宏的特殊情況?

#define IDENTITY(x) x 
#define PREPEND_ASTERISK(x) *x 
#define PREPEND_SLASH(x) /x 

IDENTITY(literal) 
PREPEND_ASTERISK(literal) 
PREPEND_SLASH(literal) 
IDENTITY(*pointer) 
PREPEND_ASTERISK(*pointer) 
PREPEND_SLASH(*pointer) 

運行gcc的預處理器就可以了:

gcc -std=c99 -E macrotest.c 

這產生了:

(...) 

literal 
*literal 
/literal 
*pointer 
**pointer 
/*pointer 

請注意,在最後一行的額外空間。

這看起來像一個功能,防止宏擴展到「/ *」給我,我敢肯定,這是一個善意的。但是一目瞭然,我在C99標準中找不到與此行爲有關的任何內容。再次,我沒有經驗C。有人可以對此有所瞭解嗎?這在哪裏指定?我猜想一個遵循C99的編譯器不應該在宏擴展期間插入額外的空格,因爲它可能會防止編程錯誤。

回答

15

源代碼在被CPP處理之前已經被標記化。

所以,你有什麼是/*令牌不會被隱式組合成一個/*「令牌」(因爲/ *是不是一個真正的預處理記號我把它放在「」)。

如果您使用-E輸出預處理源,CPP需要插入一個空格以避免/*被隨後的編譯器通過讀取。

相同的特徵防止兩個例如+來自不同宏的符號在輸出中組合成++令牌。

真正粘貼2預處理程序標記一起與##運營商的唯一方法:

#define P(x,y) x##y 

... 

P(foo,bar) 

導致令牌foobar

P(+,+) 

導致令牌++,但

P(/,*)  

自以來無效不是有效的預處理器令牌。

+0

+1。我認爲關鍵的見解是,-E的輸出確實沒有被標準規定。該標準討論了由一系列預處理令牌組成的程序,然後將其轉換爲令牌序列。這完全取決於預處理器如何表示這些序列,並且在這種情況下如何將它們序列化爲一個* bytes *序列的文件。當然,唯一可行的序列化是可以作爲等價的一系列預處理令牌讀回來的,所以,如你所說,它必須在兩個令牌之間放置空白,這兩個令牌形成一個。 – 2010-11-12 14:14:36

+0

我同意100%,想寫一些像你的解釋,但沒有時間。 – 2010-11-12 15:28:55

+4

好的答案,雖然我有兩個nitpicky的評論:沒有'''* *'標記的東西;在標記之前,註釋會從源中刪除。你可以使用'##'從兩個'+'令牌形成'++'標記。 – 2010-11-12 16:40:47

5

預處理器的行爲是標準化的。在http://en.wikipedia.org/wiki/C_preprocessor的摘要中,您觀察的結果是效果如下:

「3:標記化 - 預處理程序將結果分解爲預處理標記和空白,並用空白替換註釋」。

這發生在:

「4:宏擴展和指令處理」。