为什么 C 预处理器认为枚举值相等?
Why does the C preprocessor consider enum values as equal?
即使 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
(如注释掉的那样),我将得不到预期的输出。
提问原因:
我想要一些测试代码的模式选择器,我可以通过顶部的 commenting/uncommenting 行轻松更改模式:
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
但这会降低代码的可读性。
有没有优雅的解决方案?
预处理器在编译器之前运行,这意味着预处理器对编译器定义的符号一无所知,因此它不能根据它们采取行动。
这是因为预处理器在编译之前工作。
由于枚举定义发生在编译时,A 和 B 都将被定义为空 (pp-number 0
) - 因此在 pre-processing 时相等,因此输出语句包含在编译代码中。
当您使用 #define
时,它们在 pre-processing 时的定义不同,因此该语句的计算结果为 false。
关于你对你想做什么的评论,你不需要使用 pre-processor #if
来做到这一点。您可以只使用标准 if
,因为 MODE
和 MODE_GREY
(或 MODE_RGB
或 MODE_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;
}
另一个仅使用 pre-processor 的选项是作为 @TripeHound .
执行此操作
在条件预处理器指令中,未定义宏的标识符被解释为值 0。因此,由于您没有定义宏 A
和 B
,它们都被认为是 0,两个 0 彼此相等。
未定义(对 pre-processor)标识符被视为 0 的原因是因为它允许在条件中使用未定义的宏而不使用 #ifdef
。
没有名为 A
或 B
的宏,因此在您的 #if
行中,A
和 B
被替换为 0
,所以你实际上有:
enum T { A = 1, B = 2 };
int main() {
#if (0 == 0)
std::cout << A << B;
#endif
}
预处理器在编译器了解您的 enum
之前运行。预处理器只知道宏 (#define
).
其他答案解释了为什么您尝试的方法不起作用;作为替代方案,我可能会选择:
#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
如果存在与 "real" 源代码冲突的危险,您可能需要在 RGB/GREY/CMYK 上添加前缀。
正如其他答案所说,C 预处理器看不到枚举。它期望并且只能理解宏。
根据 the C99 standard,§6.10.1(有条件的包含):
After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers are replaced with the pp-number 0
换句话说,在 #if 或 #elif 指令中,任何不能扩展的宏,因为它们没有 exist/are undefined,将表现得就像它们被定义为 0 一样,因此将始终彼此相等。
您可以在 GCC/clang 中使用警告选项 -Wundef 捕获类似这样的意外行为(您可能希望使用 -Werror=undef 使其致命)。
帖子已经解释了原因,但是保持可读性的可能解决方案可能是这样的
#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
即使 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
(如注释掉的那样),我将得不到预期的输出。
提问原因:
我想要一些测试代码的模式选择器,我可以通过顶部的 commenting/uncommenting 行轻松更改模式:
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
但这会降低代码的可读性。
有没有优雅的解决方案?
预处理器在编译器之前运行,这意味着预处理器对编译器定义的符号一无所知,因此它不能根据它们采取行动。
这是因为预处理器在编译之前工作。
由于枚举定义发生在编译时,A 和 B 都将被定义为空 (pp-number 0
) - 因此在 pre-processing 时相等,因此输出语句包含在编译代码中。
当您使用 #define
时,它们在 pre-processing 时的定义不同,因此该语句的计算结果为 false。
关于你对你想做什么的评论,你不需要使用 pre-processor #if
来做到这一点。您可以只使用标准 if
,因为 MODE
和 MODE_GREY
(或 MODE_RGB
或 MODE_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;
}
另一个仅使用 pre-processor 的选项是作为 @TripeHound
在条件预处理器指令中,未定义宏的标识符被解释为值 0。因此,由于您没有定义宏 A
和 B
,它们都被认为是 0,两个 0 彼此相等。
未定义(对 pre-processor)标识符被视为 0 的原因是因为它允许在条件中使用未定义的宏而不使用 #ifdef
。
没有名为 A
或 B
的宏,因此在您的 #if
行中,A
和 B
被替换为 0
,所以你实际上有:
enum T { A = 1, B = 2 };
int main() {
#if (0 == 0)
std::cout << A << B;
#endif
}
预处理器在编译器了解您的 enum
之前运行。预处理器只知道宏 (#define
).
其他答案解释了为什么您尝试的方法不起作用;作为替代方案,我可能会选择:
#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
如果存在与 "real" 源代码冲突的危险,您可能需要在 RGB/GREY/CMYK 上添加前缀。
正如其他答案所说,C 预处理器看不到枚举。它期望并且只能理解宏。
根据 the C99 standard,§6.10.1(有条件的包含):
After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers are replaced with the pp-number 0
换句话说,在 #if 或 #elif 指令中,任何不能扩展的宏,因为它们没有 exist/are undefined,将表现得就像它们被定义为 0 一样,因此将始终彼此相等。
您可以在 GCC/clang 中使用警告选项 -Wundef 捕获类似这样的意外行为(您可能希望使用 -Werror=undef 使其致命)。
帖子已经解释了原因,但是保持可读性的可能解决方案可能是这样的
#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