【发布时间】:2013-06-12 02:34:43
【问题描述】:
我对何时使用宏或枚举感到困惑。两者都可以用作常量,但是它们之间有什么区别,两者的优点是什么?它是否与编译器级别有关?
【问题讨论】:
-
当您这样考虑时,我认为这是一个很好的问题:在 C 中使用宏和枚举定义常量有什么区别?
我对何时使用宏或枚举感到困惑。两者都可以用作常量,但是它们之间有什么区别,两者的优点是什么?它是否与编译器级别有关?
【问题讨论】:
宏是一种预处理器,编译后的代码不知道您创建的标识符。在代码到达编译器之前,它们已经被预处理器替换。枚举是一个编译时实体,编译后的代码保留了有关符号的完整信息,这些信息在调试器(和其他工具)中可用。
首选枚举(如果可以的话)。
【讨论】:
gcc -g3,你可以编译一个gdb友好的可执行文件,保留宏的信息。
在可读性方面,枚举比宏更适合常量,因为相关的值被组合在一起。此外,enum 定义了一个新类型,因此程序的读者可以更轻松地找出可以传递给相应参数的内容。
比较
#define UNKNOWN 0
#define SUNDAY 1
#define MONDAY 2
#define TUESDAY 3
...
#define SATURDAY 7
到
typedef enum {
UNKNOWN,
SUNDAY,
MONDAY,
TUESDAY,
...
SATURDAY,
} Weekday;
这样的代码更容易阅读
void calendar_set_weekday(Weekday wd);
比这个
void calendar_set_weekday(int wd);
因为你知道可以传递哪些常量。
【讨论】:
typedef int Weekday; 因为 typedef 和枚举在 C 中都不是类型安全的(在这个意义上)...
gdb,我使用p (int)Thursday,或p (Weekday)3。
enum foo { BAR = 3, BAZ = 1 << 9 }?是的
实际上,差别不大。它们同样可以用作程序中的常量。有些人可能出于风格原因更喜欢其中一种,但我想不出任何技术上的理由来偏爱其中一种。
一个区别是宏允许您控制相关常量的整数类型。但是enum 将使用int。
#define X 100L
enum { Y = 100L };
printf("%ld\n", X);
printf("%d\n", Y); /* Y has int type */
【讨论】:
enum 常量总是int,对于更宽的类型没有这样的规则。您可能正在与 C++ 混合使用?
请注意,宏和枚举之间存在一些差异,这些属性中的任何一个都可能使它们(不)适合作为特定常量。
sizeof(int)。对于小值数组(例如 CHAR_MAX),您可能需要 char foo[] 而不是 enum foo[] 数组。enum funny_number { PI=3.14, E=2.71 }。【讨论】:
int 相同,尽管出于二进制兼容性的原因,实际上大多数编译器都会使用int。
如果宏被正确实现(即它在被替换时不会出现关联性问题),那么在 两者 都适用的情况下,宏和枚举常量之间的适用性没有太大差异,即在情况下您需要专门的有符号整数常量。
但是,一般情况下,宏提供了更灵活的功能。枚举将特定类型强加到您的常量上:它们将具有类型 int(或者,可能是更大的有符号整数类型),并且它们将始终是有符号的。使用宏,您可以使用常量语法、后缀和/或显式类型转换来生成任何类型的常量。
当您有一组紧密关联的连续整数常量时,枚举的效果最好。当您根本不关心常量的实际值时,它们特别有效,即当您只关心它们具有 一些 行为良好的唯一值时。在所有其他情况下,宏是更好的选择(或者基本上是唯一的选择)。
【讨论】:
在 C 中,最好使用枚举进行实际枚举:当某个变量可以保存多个可以命名的值之一时。枚举的一个优点是编译器可以执行一些超出语言要求的检查,例如枚举类型的 switch 语句不会丢失任何一种情况。枚举标识符也传播到调试信息中。在调试器中,您可以将标识符名称视为枚举变量的值,而不仅仅是数值。
枚举只能用于创建整数类型的符号常量的副作用。例如:
enum { buffer_size = 4096 }; /* we don't care about the type */
这种做法并没有广泛传播。一方面,buffer_size 将用作整数而不是枚举类型。调试器不会将4096 呈现为buffer_size,因为该值不会表示为枚举类型。如果您声明了一些char array[max_buffer_size];,那么sizeof array 将不会显示为buffer_size。在这种情况下,枚举常量在编译时就消失了,所以它也可能是一个宏。并且有一些缺点,比如无法控制它的确切类型。 (在某些情况下,翻译预处理阶段的输出被捕获为文本可能会有一些小的优势。宏将变成 4096,而buffer_size 将保持为buffer_size。
预处理器符号让我们可以这样做:
#define buffer_size 0L /* buffer_size is a long int */
请注意,来自 C 的 <limits.h> 中的各种值(例如 UINT_MAX)是预处理器符号而不是枚举符号,这是有充分理由的,因为这些标识符需要具有精确确定的类型。预处理器符号的另一个优点是我们可以测试它的存在,甚至可以根据它的值做出决定:
#if ULONG_MAX > UINT_MAX
/* unsigned long is wider than unsigned int */
#endif
当然,我们也可以测试枚举常量,但不能根据结果更改全局声明。
枚举也不适合位掩码:
enum modem_control { mc_dsr = 0x1, mc_dtr = 0x2, mc_rts = 0x4, ... }
这只是没有意义,因为当这些值与按位 OR 组合时,它们会产生一个超出类型的值。如果将这样的代码移植到 C++ 中,它也会让人头疼,因为 C++ 有(更多的)类型安全枚举。
【讨论】: