命令行上的C宏定义与代码中的宏定义

命令行上的C宏定义与代码中的宏定义,c,C,考虑下面的玩具程序(称之为Foo.c),并注意到#define Foo已被注释掉 #include <stdio.h> // #define FOO int main(){ #if FOO printf("Foo\n"); #else printf("Bar\n"); #endif } 但是,如果我们取消注释行#define FOO,并使用gcc-o FOO FOO.c进行编译,则会生成编译器错误 Foo.c:5:8: error: #if with no exp

考虑下面的玩具程序(称之为
Foo.c
),并注意到
#define Foo
已被注释掉

#include <stdio.h>

// #define FOO
int main(){
#if FOO
    printf("Foo\n");
#else
    printf("Bar\n");
#endif
}
但是,如果我们取消注释行
#define FOO
,并使用
gcc-o FOO FOO.c
进行编译,则会生成编译器错误

Foo.c:5:8: error: #if with no expression
 #if FOO
        ^
  • 命令行上的
    -DFOO
    与代码中的
    #define FOO
    有什么区别
  • 为什么命令行定义可以工作,但内联定义会导致编译器错误
  • 注意:我知道这种行为可以通过
    \ifdef
    和/或
    \define FOO 1
    纠正。此问题的目的是要求解释,而不是解决问题。

    使用#ifdef而不是#if检查宏是否已定义。否则,您可以将宏更改为#define FOO 0或#define FOO 1以启用/禁用代码

    例如:

    #include <stdio.h>
    
    // #define FOO
    int main(){
    #ifdef FOO
        printf("Foo\n");
    #else
        printf("Bar\n");
    #endif
    }
    
    #包括
    //#定义FOO
    int main(){
    #ifdef FOO
    printf(“Foo\n”);
    #否则
    printf(“Bar\n”);
    #恩迪夫
    }
    
    在GCC命令行中,
    -DFOO
    -DFOO=1
    的缩写。证明:

    #include <stdio.h>
    
    // #define FOO
    int main(){
    #if FOO
        printf("Foo %d\n", FOO);
    #else
        printf("Bar %d\n", FOO);
    #endif
    }
    
    这实际上是POSIX标准的要求

    -D name[=value]

    通过C语言
    #Define
    指令定义名称。如果未给出
    =值
    ,则应使用值1。
    -D
    选项的优先级低于
    -U
    选项。也就是说,如果在
    -U
    -D
    选项中都使用了
    名称
    ,则无论选项的顺序如何,
    名称
    都应是未定义的

    因此,它基本上可以在任何地方工作。

    线路

    #define FOO
    
    表示将
    FOO
    预处理为空字符串。 行
    #if FOO
    变为
    #if
    ,这当然是一个语法错误,错误消息“#if with no expression”表明了这一点

    当命令行上不存在
    -DFOO
    时,
    FOO
    不被定义为任何东西,因此
    #if FOO
    行仍然是
    #if FOO
    在预处理的替换阶段之后(未明确定义的
    FOO
    等符号在
    #if
    条件内计算为
    0
    ). 当出现
    -DFOO
    时,它会使
    FOO
    替换为
    1
    ,如果替换后FOO在语法上有效,则再次保留行

    命令行选项
    -DFOO
    的等效定义是
    \define FOO 1


    根据C99的6.10.1p3,定义FOO的等效命令行选项是
    -DFOO=
    (谈论“if”和“elif”):

    由于宏扩展和定义的一元运算符而进行的所有替换完成后,所有剩余的标识符将替换为pp编号0,然后每个预处理令牌将转换为令牌

    因此,
    FOO
    ,如果未定义,则根据此规则变成文本
    0
    ,如果FOO
    没有给出错误,则变成
    。但是,当您定义FOO时,它会扩展为零(正如您定义的那样),并且您会得到一个错误


    GCC的
    -DFOO
    的行为与您期望的不完全一样(比如
    #define FOO
    )-它给它一个默认值
    1
    。(编辑:显然这是POSIX强制要求的…,请参见Jonathan Leffler的回答。)

    当编译一个C程序时,C编译器会多次遍历您的代码。第一个过程称为预处理器扩展阶段
    #define
    的工作原理非常类似于文字处理器的复制和粘贴。当编译器到达这一行时:

    #define FOO
    
    #if FOO
    
    编译器像内部字典一样保存名称和值。它接受
    FOO
    (名称)并在字典中将其与其后的任何文本(值)相关联。在本例中,后面没有值,因此将存储一个空字符串作为值

    当编译器到达这一行时:

    #define FOO
    
    #if FOO
    
    编译器在其字典中查找名称
    FOO
    ,并说“嗯,我的字典中有一个名为
    FOO
    ”的名称。如果没有,则会发生错误。不过,在本例中,字典中有一个
    FOO
    名称,它用与其相关联的任何值(空字符串)替换名称
    FOO
    。所以这条线变成了这样:

    #if
    
    编译器完成预处理器扩展阶段后,将再次运行代码,执行预处理器指令(如
    #if
    )。当它到达扩展的
    #if
    行时,它无效,因为
    #if
    后面没有任何内容,编译器不知道如何处理它

    为了使代码有效,有两个修复程序。一种是这样定义你的
    FOO

    #define FOO 1
    
    #if 1
    
    通过这样做,预处理器扩展阶段会将行扩展为如下所示:

    #define FOO 1
    
    #if 1
    
    因为C中的
    1
    被解释为true,所以它可以很好地编译

    第二种修复方法是将
    #if
    更改为
    #ifdef
    #ifdef
    是一个预处理器指令,如果定义了
    FOO
    ,则该指令的计算结果为true。它的定义与此无关。无论值是多少,它都将返回true。

    #如果期望表达式,而Foo不是表达式。 看看这个例子


    在您的情况下,必须使用#ifdef和#ifndef

    我的问题不是如何修复它。我的问题是为什么观察到的行为会发生。这是唯一真正解决我问题的答案!将在几分钟内接受,但如果您手头有一份简写文档参考,也将不胜感激。这将解决问题的一半,但不能解决问题的另一半,这就是为什么
    -DFOO
    工作正常的原因。@merlin2011我正忙着撰写您的评论。具体来说,我正忙于使用
    gcc-C-E test.C检查事实