是什么让C,宏或枚举更好?

Var*_*ani 34 c macros enums

我很困惑何时使用宏或枚举.两者都可以用作常量,但它们之间有什么区别,哪一个有什么优势?它是否与编译器级别有关?

das*_*ght 36

就可读性而言,枚举比宏更好,因为相关值被组合在一起.另外,enum定义一个新类型,因此程序的读者可以更容易地确定可以传递给相应参数的内容.

相比

#define UNKNOWN  0
#define SUNDAY   1
#define MONDAY   2
#define TUESDAY  3
...
#define SATURDAY 7
Run Code Online (Sandbox Code Playgroud)

typedef enum {
    UNKNOWN
,   SUNDAY
,   MONDAY
,   TUESDAY
,   ...
,   SATURDAY
} Weekday;
Run Code Online (Sandbox Code Playgroud)

阅读这样的代码要容易得多

void calendar_set_weekday(Weekday wd);
Run Code Online (Sandbox Code Playgroud)

比这个

void calendar_set_weekday(int wd);
Run Code Online (Sandbox Code Playgroud)

因为你知道哪些常数可以通过.

  • 顺便说一句,在枚举列表中的最后一项之后使用逗号是合法的,因此不需要奇怪的逗号格式. (22认同)
  • @ H2CO3:在`gdb`中,我使用`p(int)Thursday`或`p(Weekday)3`. (8认同)
  • 确实,但是人们可以同样坚持使用宏,然后`typedef int Weekday;`因为typedef和enums都不是C中的类型安全(在这个意义上)... (4认同)
  • @VarunChhangani就像`enum foo {BAR = 3,BAZ = 1 << 9}`?是 (2认同)
  • @jamesdlin 我最近发现它在 ANSI C 中*不* 合法。这让我感到惊讶(我一直认为它的主要优点是它使代码生成变得更容易一些)但是你已经做到了。 (2认同)
  • @Thomas 嗯,有趣。你是对的; C99 中 *enum-specifier* 的语法明确允许尾随逗号,但 C89 规范不包括这一点。 (2认同)

Zif*_*ion 19

宏是预处理器的事情,编译后的代码不知道您创建的标识符.在代码到达编译器之前,它们已经被预处理器替换.枚举是编译时实体,编译后的代码保留有关该符号的完整信息,该信息在调试器(和其他工具)中可用.

喜欢枚举(当你可以).

  • 没有争论,但使用`gcc -g3`,您可以编译一个gdb友好的可执行文件,保留有关宏的信息. (2认同)

Kaz*_*Kaz 11

在C中,最好使用枚举来实际枚举:当某个变量可以包含多个值中的一个时,可以给出名称.枚举的一个优点是编译器可以执行超出语言要求的某些检查,例如枚举类型上的switch语句不会丢失其中一种情况.枚举标识符也传播到调试信息中.在调试器中,您可以将标识符名称视为枚举变量的值,而不仅仅是数值.

枚举可以仅用于创建整数类型的符号常量的副作用.例如:

enum { buffer_size = 4096 };  /* we don't care about the type */
Run Code Online (Sandbox Code Playgroud)

这种做法并没有那么广泛.首先,buffer_size它将用作整数而不是枚举类型.调试器将不会呈现4096buffer_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 */
Run Code Online (Sandbox Code Playgroud)

请注意,由C的各种值<limits.h>就像UINT_MAX是预定义和不枚举符号,具有充分的理由,因为这些标识符需要有一个精确确定的类型.预处理器符号的另一个优点是我们可以测试它的存在,甚至根据它的值做出决定:

#if ULONG_MAX > UINT_MAX 
/* unsigned long is wider than unsigned int */
#endif
Run Code Online (Sandbox Code Playgroud)

当然,我们也可以测试枚举常量,但不能以我们可以根据结果更改全局声明的方式.

枚举也不适用于位掩码:

enum modem_control { mc_dsr = 0x1, mc_dtr = 0x2, mc_rts = 0x4, ... }
Run Code Online (Sandbox Code Playgroud)

它只是没有意义,因为当值与按位OR组合时,它们会产生一个超出类型的值.这样的代码也会引起头疼,如果它被移植到C++,它有(更多)类型安全的枚举.


Jen*_*ens 9

请注意,宏和枚举之间存在一些差异,这些属性中的任何一个都可能使它们(un)适合作为特定常量.

  • 枚举已签名(与int兼容).在任何需要无符号类型的上下文中(特别考虑按位操作!),枚举就出来了.
  • 如果long long比int宽,则大常量将不适合枚举.
  • 枚举的大小是sizeof(int).对于小值数组(最多可以说CHAR_MAX),您可能需要一个char foo[]而不是一个enum foo[]数组.
  • 枚举是整数.你不能拥有enum funny_number { PI=3.14, E=2.71 }.
  • 枚举是C89的功能; K&R编译器(公认的古老)不理解它们.

  • 实际上,枚举的大小是[*not*guarantee](http://stackoverflow.com/a/366033/440302)与`int`相同,尽管实际上大多数编译器都会使用`int`来实现二进制兼容性. (2认同)

jxh*_*jxh 5

实际上,差别不大。它们同样可以用作程序中的常量。有些人可能出于风格原因更喜欢其中一种,但我想不出任何技术原因来选择其中一种。

一个区别是宏允许您控制相关常量的整数类型。但 anenum将使用int.

#define X 100L
enum { Y = 100L };

printf("%ld\n", X);
printf("%d\n", Y); /* Y has int type */
Run Code Online (Sandbox Code Playgroud)