为什么C预处理器将枚举值视为相等?

时间:2016-01-08 12:27:39

标签: c++ c-preprocessor

为什么即使std::coutA不同,以下代码中的B行也会运行?

#include <iostream>

enum T { A = 1, B = 2 };
// #define A 1
// #define B 2

int main() {
#if (A == B)
    std::cout << A << B;
#endif
}

如果我使用#define代替(注释掉),我没有得到任何预期的输出。

问题的原因:

我希望有一个模式选择器用于某些测试代码,我可以通过在顶部注释/取消注释行来轻松更改模式:

enum T { MODE_RGB = 1, MODE_GREY = 2, MODE_CMYK = 3 };
// #define MODE MODE_RGB
#define MODE MODE_GREY
// #define MODE MODE_CMYK

int main() {
#if (MODE == MODE_RGB)
    // do RGB stuff
#elif (MODE == MODE_GREY)
    // do greyscale stuff
#else
    // do CMYK stuff
#endif

    // some common code

    some_function(arg1, arg2,
#if (MODE == MODE_RGB)
        // RGB calculation for arg3,
#elif (MODE == MODE_GREY)
        // greyscale calculation for arg3,
#else
        // CMYK calculation for arg3,
#endif
        arg4, arg5);
}

我知道我可以使用数值,例如

#define MODE 1 // RGB
...
#if (MODE == 1) // RGB

但它使代码的可读性降低。

对此有优雅的解决方案吗?

7 个答案:

答案 0 :(得分:119)

没有名为AB的宏,因此在#if行,AB0取代,所以你实际上有:

enum T { A = 1, B = 2 };

int main() {
#if (0 == 0)
    std::cout << A << B;
#endif
}

预编译器在编译器知道您的enum之前运行。预处理器只知道宏(#define)。

答案 1 :(得分:44)

这是因为预处理器在编译时之前工作。

由于枚举定义发生在编译时,A和B都将被定义为空(pp-number 0) - 因此在预处理时间相等 - 因此输出语句包含在编译后的代码。

使用#define时,它们在预处理时的定义不同,因此语句的计算结果为false。

关于您对要执行的操作的评论,您无需使用预处理器#if来执行此操作。您可以使用标准if,因为MODEMODE_GREY(或MODE_RGBMODE_CMYK)都已定义:

#include <iostream>

enum T { MODE_RGB = 1, MODE_GREY = 2, MODE_CMYK = 3 };

#define MODE MODE_GREY

int main()
{
    if( MODE == MODE_GREY )
        std::cout << "Grey mode" << std::endl;
    else if( MODE == MODE_RGB )
        std::cout << "RGB mode" << std::endl;
    else if( MODE == MODE_CMYK )
        std::cout << "CMYK mode" << std::endl;

    return 0;
}

仅使用预处理器的另一个选项是@TripeHound correctly answered below

答案 2 :(得分:24)

未定义宏的标识符在条件预处理程序指令中被解释为值0。因此,由于您没有定义宏AB,因此它们都被认为是0而两个0彼此相等。

未定义(对于预处理器)标识符被认为是0的原因是因为它允许在条件中使用未定义的宏而不使用#ifdef

答案 3 :(得分:11)

预处理器在编译器之前运行,这意味着预处理器对编译器定义的符号一无所知,因此它不能依赖于它们。

答案 4 :(得分:10)

正如其他答案所说,C预处理器没有看到枚举。它期望并且只能理解宏。

Per the C99 standard,§6.10.1(条件包含):

  

在完成由于宏扩展和已定义的一元运算符而导致的所有替换后,所有剩余的标识符将替换为pp-number 0

换句话说,在#if或#elif指令中,任何无法展开的宏,因为它们不存在/未定义,其行为就像它们被定义为0一样,因此总是如此相互平等。

您可以使用警告选项-Wundef捕获GCC / clang中可能出现的意外行为(您可能希望使用-Werror = undef使其致命)。

答案 5 :(得分:7)

其他答案解释了为什么你所尝试的不起作用;换句话说,我可能会选择:

#define RGB 1
#define GREY 2
#define CMYK 3
#define MODE RGB

#if MODE == RGB
    //RGB-mode code
#elif MODE == GREY
    //Greyscale code
#elif MODE == CMYK
    //CMYK code
#else
#    error Undefined MODE
#endif

如果存在与“真实”源代码发生冲突的危险,您可能需要RGB / GRAY / CMYK上的前缀。

答案 6 :(得分:2)

这些帖子已经解释了原因,但是一个可以保持可读性的解决方案可能就像这样

#define MODE_RGB

int main()
{        
    #ifdef MODE_RGB
        std::cout << "RGB mode" << std::endl;
    #elif defined MODE_GREY
        std::cout << "Grey mode" << std::endl;
    #elif defined MODE_CMYK
        std::cout << "CMYK mode" << std::endl;
    #endif
}

然后您需要更改顶部的宏,只定义您感兴趣的宏。您还可以添加一项检查以确保定义了一个且仅定义了一个,如果没有,则执行#error "You must define MODE_RGB, MODE_GREY or MODE_CMYK