我对何时使用宏或枚举感到困惑。两者都可以用作常量,但是它们之间有什么区别,两者的优点是什么?它是否与编译器级别有关?
7 回答
在可读性方面,枚举比宏更好的常量,因为相关的值被组合在一起。此外,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);
因为您知道可以传递哪些常量。
宏是预处理器,编译后的代码不知道您创建的标识符。在代码到达编译器之前,它们已经被预处理器替换。枚举是一个编译时实体,编译后的代码保留了有关符号的完整信息,这些信息在调试器(和其他工具)中可用。
更喜欢枚举(如果可以的话)。
在 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++ 中,此类代码也会令人头疼。
请注意,宏和枚举之间存在一些差异,这些属性中的任何一个都可能使它们(不)适合作为特定常量。
- 枚举已签名(与 int 兼容)。在任何需要无符号类型的上下文中(尤其是按位运算!),枚举都不存在。
- 如果 long long 比 int 宽,则大常量将不适合枚举。
- 枚举的大小是(通常)
sizeof(int)
。对于小值的数组(最多说,CHAR_MAX
),您可能需要 achar foo[]
而不是enum foo[]
数组。 - 枚举是整数。你不能有
enum funny_number { PI=3.14, E=2.71 }
。 - 枚举是 C89 的一个特性;K&R 编译器(诚然是古老的)不理解它们。
如果正确实现了宏(即在被替换时不会出现关联性问题),那么在两者都适用的情况下,即在您需要有符号整数常量的情况下,宏和枚举常量之间的适用性没有太大差异。
但是,在一般情况下,宏提供了更灵活的功能。枚举将特定类型强加到您的常量上:它们将具有类型int
(或者,可能是更大的有符号整数类型),并且它们将始终是有符号的。使用宏,您可以使用常量语法、后缀和/或显式类型转换来生成任何类型的常量。
当您有一组紧密关联的顺序整数常量时,枚举的效果最好。当您根本不关心常量的实际值时,它们特别有效,即当您只关心它们具有一些行为良好的独特值时。在所有其他情况下,宏是更好的选择(或者基本上是唯一的选择)。
实际上,差别不大。它们同样可以用作程序中的常量。有些人可能出于风格原因更喜欢其中一种,但我想不出任何技术上的理由来偏爱其中一种。
一个区别是宏允许您控制相关常量的整数类型。但是 anenum
将使用int
.
#define X 100L
enum { Y = 100L };
printf("%ld\n", X);
printf("%d\n", Y); /* Y has int type */
enum
有一个优点:块范围:
{ enum { E = 12 }; }
{ enum { E = 13 }; }
使用宏需要#undef
.