“真”和“假”在预处理器条件中是否具有通常的含义?
给定一个 C++11 编译器,#error它最终应该使用哪个正确的编译器?
// no #includes!
#define SOMEMACRO true
#if SOMEMACRO
#error "it was true"
#else
#error "it was false"
#endif
Godbolt 演示
显然我#error只是作为测试使用。我知道true并false以适当的语言定义,但这是预处理器上下文。在 C99 中,它似乎不被预处理器识别。
我问是因为似乎我尝试过的所有编译器都将其视为“真”,而静态代码分析工具坚持认为true未定义,隐含错误并以“它是假的”结束。
回答
在所有 ISO C++ 标准中,true和false都是关键字常量,就像nullptr在 C++11 中一样。所以#if SOMEMACRO=#if true并且预处理器将转到真实分支。
然而,在 C 中,既不是关键字true也不false是关键字。它们是分别定义为1和 的宏0,从 C99 开始,并带有#include <stdbool.h>. 这不等于说但是,如果不包括stdbool.h,编译器应该抱怨无法识别的标识符true,false等等,包括头之后,#if SOMEMACRO现在#if 1,这是truthy在C.
对于预处理,来自CppReference 的引用是有意义的:
任何不是文字的、非使用
#define指令定义的标识符的计算结果为0。
因此,在您的(可能是面向 C 的)静态分析工具中,它被true视为#define未定义的标识符,因此计算结果true为零。如果您使用 C++ 分析工具,您将不会观察到这种行为。
在这种情况下,您可能一开始就不应该错过#include <stdbool.h>。
- For reference, `true` and `false` are [specifically singled out](https://timsong-cpp.github.io/cppwp/n3337/cpp.cond#4) to not be replaced when other identifiers are replaced. `nullptr` is a literal, but it doesn't get this treatment, which you can see with a condition such as `#if nullptr + 2 == 2` that would fail to compile if `nullptr` were not replaced with `0`.
回答
根据C++11 标准中的[cpp.cond]/4 :
在评估之前,将成为控制常量表达式的预处理标记列表中的宏调用被替换(除了那些被
defined一元运算符修改的宏名称),就像在普通文本中一样。[…] 由于宏扩展和defined一元运算符的所有替换都执行完毕后,除trueand之外的false所有剩余标识符和关键字都替换为 pp-number0,然后将每个预处理标记转换为一个标记。结果标记包括控制常量表达式,该表达式根据 [expr.const] 的规则使用至少具有 [support.limits] 中指定的范围的算术进行评估。[…] 每个带有类型的子表达式bool在继续处理之前进行积分提升。
强调我的;从粗体段落可以看出,bool-typed 表达式旨在在预处理器条件中得到支持,就像在适当的语言中一样,包括bool文字true和false. 定义常量表达式的 [expr.const] 部分是从在非预处理上下文中使用它的其他部分引用的,从这些部分可以看出,预处理器和语言本身中的评估规则是相同的。
我认为类似的语言出现在 C++ 标准的所有进一步修订版中,也可能出现在更早的版本中。另一方面,在 C 中,trueandfalse不是关键字,而是定义在 中的宏stdbool.h,因此预处理器将它们视为任何其他标记。
通常的做法是在预处理器表达式中使用1和0用于逻辑值以获得最大的可移植性,并且最好避免完全直接引用它们。