为什么C预处理器将枚举值视为相等?
为什么即使A
和B
不同,为什么下面的代码中的std::cout
行仍然运行?
#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
(如注释掉),我没有得到任何输出。
问题的理由:
我想有一个testing代码的模式select器,我可以轻松地通过注释/取消注释上面的行来改变模式:
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
但是会使代码不易读。
有没有一个优雅的解决scheme呢?
没有叫做A
或B
macros,所以在你的#if
行中, A
和B
被replace为0
,所以你实际上有:
enum T { A = 1, B = 2 }; int main() { #if (0 == 0) std::cout << A << B; #endif }
预编译器在编译器知道你的enum
之前运行。 预处理器只知道macros( #define
)。
这是因为预处理器在编译之前工作。
由于枚举定义是在编译时发生的,所以在预处理时,A和B都将被定义为空(pp-number 0
) – 因此是相等的,因此输出语句被包含在编译后的代码中。
当你使用#define
它们在预处理时被定义为不同的,因此语句的计算结果为false。
关于你想要做什么的评论,你不需要使用预处理器#if
来做到这一点。 if
MODE
和MODE_GREY
(或者MODE_RGB
或者MODE_CMYK
)都是定义的, if
你可以使用这个标准:
#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。
未定义macros的标识符在条件预处理器指令中被解释为值0。 因此,由于您没有定义macrosA
和B
,所以它们都被认为是0,并且两个0彼此相等。
未定义(对于预处理器)标识符被视为0的原因是因为它允许在条件中使用未定义的macros而不使用#ifdef
。
预处理器在编译器之前运行,这意味着预处理器不知道编译器定义的符号的任何内容,因此它不能根据它们而行动。
正如其他答案所说,C预处理器不会看到枚举。 它期望并且只能理解macros。
根据C99标准 ,§6.10.1(有条件包含):
在由于macros扩展和所定义的一元运算符的所有replace之后,所有剩余的标识符被replace为pp-number0
换句话说,在#if或#elif指令中,任何不能被扩展的macros,因为它们不存在/是未定义的,它们的行为就像被定义为0一样,因此总是等于彼此。
你可以在GCC / clang中使用警告选项-Wundef捕获像这样的非预期行为(你可能想用-Werror = undef使它变成致命的)。
其他答案解释了为什么你正在尝试不工作; 另外,我可能会去:
#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 / GREY / CMYK上的前缀。
这些post解释了为什么,但是为了保持可读性,一个可能的解决scheme可能就是这样
#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 }
您只需要在顶部更改macros,即只定义您感兴趣的macros。 你也可以包括一个检查,以确保只有一个被定义,如果没有,然后做#error "You must define MODE_RGB, MODE_GREY or MODE_CMYK