3

我发现了一些预处理指令,其中宏名称和替换列表是相同的。例如,gcc 提供的 stdbool.h 中的一些预处理指令。

#else /* __cplusplus */

/* Supporting <stdbool.h> in C++ is a GCC extension.  */
#define _Bool   bool
#define bool    bool
#define false   false
#define true    true

#endif /* __cplusplus */

我不明白程序员为什么要编写这些预处理指令。它们没用,替换会浪费时间。我知道这不会导致无限递归。如何避免无限递归?C标准中有哪些相关规定?

4

4 回答 4

5

C++11 标准第 16.3.4 节第 2 段防止了宏替换期间的无限递归:

如果在替换列表的扫描过程中找到被替换的宏的名称(不包括源文件的其余预处理标记),则不会替换它。此外,如果任何嵌套替换遇到被替换的宏的名称,它不会被替换。这些未替换的宏名称预处理标记不再可用于进一步替换,即使它们稍后在该宏名称预处理标记将被替换的上下文中进行(重新)检查。

基本上,这意味着出现在其自身扩展中的宏不会再次被替换。

trueGCC 扩展将、boolfalse定义为宏的原因是为了使 C++ 代码与 C99 更兼容。在 C99 中,这些在 stdbool.h 中定义为宏,因此代码可以检查它们是否使用 eg 定义#ifdef bool

于 2012-09-04T12:34:01.297 回答
2

除了其他答案之外,定义这些宏将帮助编译器生成“宏重新定义”错误消息,如果任何人、任何地方都有重新定义布尔值、真或假的绝妙主意。

请参阅标准第 16.3 节 - 在宏重新定义中只允许相同的替换列表。

于 2012-09-04T12:36:07.657 回答
1

有时标准说“X必须是宏”。如果你的语言已经有一个内在的X,你必须说#define X X是标准兼容的。(例如,用户可以说#ifdef X并期望这是真的。)

示例(C11,7.2/2):

assert宏应实现为宏,而不是实际功能。如果为了访问实际函数而抑制宏定义,则行为未定义。

您的实现具有实际assert功能是完全合理的。

为了好玩,这里有一些关于它们是宏还是具有外部链接的标识符的“未指定”的东西:

  • errno

  • setjmp

  • va_copy,va_end

  • putc,getc

于 2012-09-04T12:29:59.993 回答
1

宏扩展不会递归,因此它们将终止。

拥有宏的一个优点是您可以在预处理器中对其进行测试

#if defined bool
...
#endif

仅在bool是宏时才有效。

于 2012-09-04T12:30:57.250 回答