2010-11-12 85 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:宏扩展和指令处理”。