当一个宏被定义为0时,#ifdef和#if之间微妙的区别是什么?

7

以下是给定的C文件:

$ cat macros.c
#ifdef MACRO
#  error MACRO is defined
#else
#  error MACRO is undefined
#endif
#if MACRO
#  error MACRO is non-zero
#else
#  error MACRO is zero
#endif

以下代码的预期输出是什么?
$ gcc           -c macros.c
$ gcc -DMACRO   -c macros.c
$ gcc -DMACRO=0 -c macros.c

答案: 这是我的机器上gcc的预处理器所做的事情。

$ gcc           -c macros.c
macros.c:4:4: error: #error MACRO is undefined
macros.c:9:4: error: #error MACRO is zero
$ gcc -DMACRO   -c macros.c
macros.c:2:4: error: #error MACRO is defined
macros.c:7:4: error: #error MACRO is non-zero
$ gcc -DMACRO=0 -c macros.c
macros.c:2:4: error: #error MACRO is defined
macros.c:9:4: error: #error MACRO is zero
$
课程#ifdef MACRO 对于已定义的情况下即使定义值为0(零)也会评估为真(true)。这是另一个C预处理器需要注意的地方!这是否符合C标准?

3
我对“微妙”的差异或任何形式的“陷阱”提出质疑。对我来说,它似乎相当清晰和直观。#ifdef代表“如果定义”。你为什么认为值与此有关呢?也许是来自PHP / Javascript懒惰比较的影响? - Lightness Races in Orbit
2
定义一个宏来具有值0,或fred""或其他任何值,实际上仍然是定义该宏,因此在我看来,如果已经定义了该宏,#ifdef应该报告该宏已定义-它不关心它的定义值是什么,只要它已经被定义... - twalberg
你可以通过阅读标准来回答你的问题,为什么要求别人替你阅读呢? - Eric Lippert
3个回答

9
任何未定义的宏在评估 #if 语句的控制表达式时都被视为已定义为0。来自 C99 §6.10.1/3-4(强调添加):
3) 预处理指令的形式为 # if constant-expression new-line groupopt # elif constant-expression new-line groupopt 检查控制常量表达式是否计算为非零。
4) 在求值之前,预处理标记列表中将成为控制常量表达式的宏调用(除了那些由 defined 一元运算符修改的宏名称)被替换,就像在普通文本中一样。如果标记 defined 作为此替换过程或使用 defined 一元运算符不匹配两个指定形式之一,则行为未定义。在执行由于宏扩展和 defined 一元运算符引起的所有替换后,所有剩余的标识符(包括那些在词汇上等同于关键字的标识符)都被替换为 pp-number 0,然后将每个预处理标记转换为一个标记。[...]
因此,例如,像这样的表达式:
#if !FOO

如果FOO未定义,则评估为1,因为它将被视为0,然后!FOO将被评估为!0,即1


7

#ifdef 只关心 MACRO 是否已经被定义,其值并不重要。

#if 检查 MACRO 的值并相应地评估 MACRO。

这是正确的行为。


2
这并没有回答在 #if 语句中未定义的标识符如何计算的问题。 - Eric Postpischil
3
这在C和C++标准中已经明确定义了 - 没有被 #define 宏定义的标识符在 #if 或 #elif 语句中会被替换为0。这应该是常识。 - gnasher729
我可以确认,当符号未定义时,#if 的行为与定义为 0 值时相同。 - micnguyen

2

这种行为符合标准要求。

C99标准:6.10.1 条件包含:

第2段:预处理指令的形式

# if constant-expression new-line groupopt
# elif constant-expression new-line groupopt

请检查控制常量表达式是否评估为非零。

第4段:以以下形式的预处理指令

# ifdef identifier new-line groupopt
# ifndef identifier new-line groupopt

检查标识符是否已定义为宏名称。它们的条件分别等同于#if defined标识符#if !defined标识符


2
这并没有回答在#if语句中未定义标识符的表达式是如何被评估的这个问题。 - Eric Postpischil
请阅读上面的答案或C标准。谷歌搜索N1570以下载最新的免费标准草案。 - gnasher729
但是我们可以使用 #ifdef#elif。如果我们为 #ifdef 情况下定义宏值为 0,则 #ifdef 情况将测试其为真。否则,如果我们为 #elif 情况下定义宏值为 0,则 #elif 情况将测试其为 - rosshjb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接