在C、宏或enum中,什么是更好的常量?

时间:2023-01-15 10:41:20

I am confused about when to use macros or enums. Both can be used as constants, but what is the difference between them and what is the advantage of either one? Is it somehow related to compiler level or not?

我不知道什么时候使用宏或枚举。两者都可以用作常量,但是它们之间的区别是什么?它是否与编译器级别相关?

6 个解决方案

#1


26  

In terms of readability, enumerations make better constants than macros, because related values are grouped together. In addition, enum defines a new type, so the readers of your program would have easier time figuring out what can be passed to the corresponding parameter.

在可读性方面,枚举比宏更适合作为常量,因为相关值被分组在一起。此外,enum定义了一个新类型,因此程序的读者可以更容易地确定可以向相应的参数传递什么。

Compare

比较

#define UNKNOWN  0
#define SUNDAY   1
#define MONDAY   2
#define TUESDAY  3
...
#define SATURDAY 7

to

typedef enum {
    UNKNOWN
,   SUNDAY
,   MONDAY
,   TUESDAY
,   ...
,   SATURDAY
} Weekday;

It is much easier to read code like this

像这样读代码要容易得多

void calendar_set_weekday(Weekday wd);

than this

比这

void calendar_set_weekday(int wd);

because you know which constants it is OK to pass.

因为你知道哪些常数可以传递。

#2


18  

A macro is a preprocessor thing, and the compiled code has no idea about the identifiers you create. They have been already replaced by the preprocessor before the code hits the compiler. An enum is a compile time entity, and the compiled code retains full information about the symbol, which is available in the debugger (and other tools).

宏是预处理程序,编译后的代码不知道您创建的标识符。在代码到达编译器之前,它们已经被预处理器所取代。enum是一个编译时实体,编译后的代码保留有关符号的完整信息,可以在调试器(和其他工具)中使用。

Prefer enums (when you can).

如果可以的话,我更喜欢念哦。

#3


8  

Note there are some differences between macros and enums, and either of these properties may make them (un)suitable as a particular constant.

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

  • enums are signed (compatible with int). In any context where an unsigned type is required (think especially bitwise operations!), enums are out.
  • 枚举被签名(与int兼容)。在任何需要无符号类型的上下文中(特别是按位运算!),enums已经退出。
  • if long long is wider than int, big constants won't fit in an enum.
  • 如果长长长比int要宽,那么大的常量就不能在enum中使用。
  • The size of an enum is sizeof(int). For arrays of small values (up to say, CHAR_MAX) you might want a char foo[] rather than an enum foo[] array.
  • 枚举的大小是sizeof(int)。对于小值数组(比如CHAR_MAX),您可能需要char foo[]而不是enum foo[]数组。
  • enums are integral numbers. You can't have enum funny_number { PI=3.14, E=2.71 }.
  • 枚举是积分数字。不能有enum funny_number {PI=3.14, E=2.71}。
  • enums are a C89 feature; K&R compilers (admittedly ancient) don't understand them.
  • 枚举是C89特征;K&R编译器(公认是古老的)不理解它们。

#4


7  

In C, it is best to use enums for actual enumerations: when some variable can hold one of multiple values which can be given names. One advantage of enums is that the compiler can perform some checks beyond what the language requires, like that a switch statement on the enum type is not missing one of the cases. The enum identifiers also propagate into the debugging information. In a debugger, you can see the identifier name as the value of an enum variable, rather than just the numeric value.

在C语言中,最好将枚举用于实际的枚举:当某个变量可以包含多个值中的一个,这些值可以被赋予名称。枚举的一个优点是编译器可以执行一些超出语言需求的检查,比如enum类型上的switch语句没有遗漏任何一种情况。enum标识符也传播到调试信息中。在调试器中,可以将标识符名称视为enum变量的值,而不仅仅是数值。

Enumerations can be used just for the side effect of creating symbolic constants of integral type. For instance:

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

enum { buffer_size = 4096 };  /* we don't care about the type */

this practice is not that wide spread. For one thing, buffer_size will be used as an integer and not as an enumerated type. A debugger will not render 4096 into buffer_size, because that value won't be represented as the enumerated type. If you declare some char array[max_buffer_size]; then sizeof array will not show up as buffer_size. In this situation, the enumeration constant disappears at compile time, so it might as well be a macro. And there are disadvantages, like not being able to control its exact type. (There might be some small advantage in some situation where the output of the preprocessing stages of translation is being captured as text. A macro will have turned into 4096, whereas buffer_size will stay as buffer_size).

这种做法并没有广泛传播。首先,buffer_size将用作整数,而不是枚举类型。调试器不会将4096呈现到buffer_size中,因为该值不会表示为枚举类型。如果声明某个char数组[max_buffer_size];然后sizeof数组将不会显示为buffer_size。在这种情况下,枚举常量在编译时消失,因此它也可能是一个宏。还有一些缺点,比如不能精确控制它的类型。(在某些情况下,翻译的预处理阶段的输出被捕获为文本可能有一些小的优势。宏将变成4096,而buffer_size将保持为buffer_size)。

A preprocessor symbol lets us do this:

一个预处理器符号让我们这样做:

#define buffer_size 0L /* buffer_size is a long int */

Note that various values from C's <limits.h> like UINT_MAX are preprocessor symbols and not enum symbols, with good reasons for that, because those identifiers need to have a precisely determined type. Another advantage of a preprocessor symbol is that we can test for its presence, or even make decisions based on its value:

注意,C的 是预处理器符号,而不是enum符号,这是有充分理由的,因为这些标识符需要具有精确确定的类型。预处理器符号的另一个优点是我们可以测试它的存在性,甚至可以根据它的价值做出决定: 的不同值。像uint_max这样的h>

#if ULONG_MAX > UINT_MAX 
/* unsigned long is wider than unsigned int */
#endif

Of course we can test enumerated constants also, but not in such a way that we can change global declarations based on the result.

当然,我们也可以测试枚举常量,但是不能以这样的方式来更改基于结果的全局声明。

Enumerations are also ill suited for bitmasks:

枚举也不适用于位掩码:

enum modem_control { mc_dsr = 0x1, mc_dtr = 0x2, mc_rts = 0x4, ... }

it just doesn't make sense because when the values are combined with a bitwise OR, they produce a value which is outside of the type. Such code causes a headache, too, if it is ever ported to C++, which has (somewhat more) type-safe enumerations.

这是没有意义的,因为当值与位相结合时,它们会产生一个类型之外的值。这样的代码如果被移植到c++,也会引起麻烦,因为c++有类型安全的枚举。

#5


3  

If macro is implemented properly (i.e it does not suffer from associativity issues when substituted), then there's not much difference in applicability between macro and enum constants in situations where both are applicable, i.e. in situation where you need signed integer constants specifically.

如果宏被正确地执行(我。当被替换时,它不会受到结合律问题的影响),那么在两者都适用的情况下,宏观常量和enum常量的适用性并没有太大的差异,例如在需要有符号整型常量的情况下。

However, in general case macros provide much more flexible functionality. Enums impose a specific type onto your constants: they will have type int (or, possibly, larger signed integer type), and they will always be signed. With macros you can use constant syntax, suffixes and/or explicit type conversions to produce a constant of any type.

然而,在一般情况下,宏提供了更灵活的功能。枚举将特定的类型强加给您的常量:它们将具有类型int(或者,可能是更大的带符号整数类型),并且它们将始终是有符号的。使用宏,可以使用常量语法、后缀和/或显式类型转换生成任意类型的常量。

Enums work best when you have a group of tightly associated sequential integer constants. They work especially well when you don't care about the actual values of the constants at all, i.e. when you only care about them having some well-behaved unique values. In all other cases macros are a better choice (or basically the only choice).

当您拥有一组紧密关联的连续整数常量时,枚举最有效。当你一点都不关心常数的实际值时,它们就能很好地工作,也就是说,当你只关心它们有一些行为良好的唯一值时。在所有其他情况下,宏都是更好的选择(或者基本上是唯一的选择)。

#6


1  

As a practical matter, there is little difference. They are equally usable as constants in your programs. Some may prefer one or the other for stylistic reasons, but I can't think of any technical reason to prefer one over the other.

就实际情况而言,没有什么不同。它们与程序中的常量一样可用。有些人可能出于风格上的原因喜欢其中一个,但我想不出任何技术上的原因来喜欢另一个。

One difference is that macros allow you to control the integral type of related constants. But an enum will use an int.

一个区别是宏允许您控制相关常量的积分类型。但是enum将使用int形式。

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

printf("%ld\n", X);
printf("%d\n", Y); /* Y has int type */

#1


26  

In terms of readability, enumerations make better constants than macros, because related values are grouped together. In addition, enum defines a new type, so the readers of your program would have easier time figuring out what can be passed to the corresponding parameter.

在可读性方面,枚举比宏更适合作为常量,因为相关值被分组在一起。此外,enum定义了一个新类型,因此程序的读者可以更容易地确定可以向相应的参数传递什么。

Compare

比较

#define UNKNOWN  0
#define SUNDAY   1
#define MONDAY   2
#define TUESDAY  3
...
#define SATURDAY 7

to

typedef enum {
    UNKNOWN
,   SUNDAY
,   MONDAY
,   TUESDAY
,   ...
,   SATURDAY
} Weekday;

It is much easier to read code like this

像这样读代码要容易得多

void calendar_set_weekday(Weekday wd);

than this

比这

void calendar_set_weekday(int wd);

because you know which constants it is OK to pass.

因为你知道哪些常数可以传递。

#2


18  

A macro is a preprocessor thing, and the compiled code has no idea about the identifiers you create. They have been already replaced by the preprocessor before the code hits the compiler. An enum is a compile time entity, and the compiled code retains full information about the symbol, which is available in the debugger (and other tools).

宏是预处理程序,编译后的代码不知道您创建的标识符。在代码到达编译器之前,它们已经被预处理器所取代。enum是一个编译时实体,编译后的代码保留有关符号的完整信息,可以在调试器(和其他工具)中使用。

Prefer enums (when you can).

如果可以的话,我更喜欢念哦。

#3


8  

Note there are some differences between macros and enums, and either of these properties may make them (un)suitable as a particular constant.

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

  • enums are signed (compatible with int). In any context where an unsigned type is required (think especially bitwise operations!), enums are out.
  • 枚举被签名(与int兼容)。在任何需要无符号类型的上下文中(特别是按位运算!),enums已经退出。
  • if long long is wider than int, big constants won't fit in an enum.
  • 如果长长长比int要宽,那么大的常量就不能在enum中使用。
  • The size of an enum is sizeof(int). For arrays of small values (up to say, CHAR_MAX) you might want a char foo[] rather than an enum foo[] array.
  • 枚举的大小是sizeof(int)。对于小值数组(比如CHAR_MAX),您可能需要char foo[]而不是enum foo[]数组。
  • enums are integral numbers. You can't have enum funny_number { PI=3.14, E=2.71 }.
  • 枚举是积分数字。不能有enum funny_number {PI=3.14, E=2.71}。
  • enums are a C89 feature; K&R compilers (admittedly ancient) don't understand them.
  • 枚举是C89特征;K&R编译器(公认是古老的)不理解它们。

#4


7  

In C, it is best to use enums for actual enumerations: when some variable can hold one of multiple values which can be given names. One advantage of enums is that the compiler can perform some checks beyond what the language requires, like that a switch statement on the enum type is not missing one of the cases. The enum identifiers also propagate into the debugging information. In a debugger, you can see the identifier name as the value of an enum variable, rather than just the numeric value.

在C语言中,最好将枚举用于实际的枚举:当某个变量可以包含多个值中的一个,这些值可以被赋予名称。枚举的一个优点是编译器可以执行一些超出语言需求的检查,比如enum类型上的switch语句没有遗漏任何一种情况。enum标识符也传播到调试信息中。在调试器中,可以将标识符名称视为enum变量的值,而不仅仅是数值。

Enumerations can be used just for the side effect of creating symbolic constants of integral type. For instance:

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

enum { buffer_size = 4096 };  /* we don't care about the type */

this practice is not that wide spread. For one thing, buffer_size will be used as an integer and not as an enumerated type. A debugger will not render 4096 into buffer_size, because that value won't be represented as the enumerated type. If you declare some char array[max_buffer_size]; then sizeof array will not show up as buffer_size. In this situation, the enumeration constant disappears at compile time, so it might as well be a macro. And there are disadvantages, like not being able to control its exact type. (There might be some small advantage in some situation where the output of the preprocessing stages of translation is being captured as text. A macro will have turned into 4096, whereas buffer_size will stay as buffer_size).

这种做法并没有广泛传播。首先,buffer_size将用作整数,而不是枚举类型。调试器不会将4096呈现到buffer_size中,因为该值不会表示为枚举类型。如果声明某个char数组[max_buffer_size];然后sizeof数组将不会显示为buffer_size。在这种情况下,枚举常量在编译时消失,因此它也可能是一个宏。还有一些缺点,比如不能精确控制它的类型。(在某些情况下,翻译的预处理阶段的输出被捕获为文本可能有一些小的优势。宏将变成4096,而buffer_size将保持为buffer_size)。

A preprocessor symbol lets us do this:

一个预处理器符号让我们这样做:

#define buffer_size 0L /* buffer_size is a long int */

Note that various values from C's <limits.h> like UINT_MAX are preprocessor symbols and not enum symbols, with good reasons for that, because those identifiers need to have a precisely determined type. Another advantage of a preprocessor symbol is that we can test for its presence, or even make decisions based on its value:

注意,C的 是预处理器符号,而不是enum符号,这是有充分理由的,因为这些标识符需要具有精确确定的类型。预处理器符号的另一个优点是我们可以测试它的存在性,甚至可以根据它的价值做出决定: 的不同值。像uint_max这样的h>

#if ULONG_MAX > UINT_MAX 
/* unsigned long is wider than unsigned int */
#endif

Of course we can test enumerated constants also, but not in such a way that we can change global declarations based on the result.

当然,我们也可以测试枚举常量,但是不能以这样的方式来更改基于结果的全局声明。

Enumerations are also ill suited for bitmasks:

枚举也不适用于位掩码:

enum modem_control { mc_dsr = 0x1, mc_dtr = 0x2, mc_rts = 0x4, ... }

it just doesn't make sense because when the values are combined with a bitwise OR, they produce a value which is outside of the type. Such code causes a headache, too, if it is ever ported to C++, which has (somewhat more) type-safe enumerations.

这是没有意义的,因为当值与位相结合时,它们会产生一个类型之外的值。这样的代码如果被移植到c++,也会引起麻烦,因为c++有类型安全的枚举。

#5


3  

If macro is implemented properly (i.e it does not suffer from associativity issues when substituted), then there's not much difference in applicability between macro and enum constants in situations where both are applicable, i.e. in situation where you need signed integer constants specifically.

如果宏被正确地执行(我。当被替换时,它不会受到结合律问题的影响),那么在两者都适用的情况下,宏观常量和enum常量的适用性并没有太大的差异,例如在需要有符号整型常量的情况下。

However, in general case macros provide much more flexible functionality. Enums impose a specific type onto your constants: they will have type int (or, possibly, larger signed integer type), and they will always be signed. With macros you can use constant syntax, suffixes and/or explicit type conversions to produce a constant of any type.

然而,在一般情况下,宏提供了更灵活的功能。枚举将特定的类型强加给您的常量:它们将具有类型int(或者,可能是更大的带符号整数类型),并且它们将始终是有符号的。使用宏,可以使用常量语法、后缀和/或显式类型转换生成任意类型的常量。

Enums work best when you have a group of tightly associated sequential integer constants. They work especially well when you don't care about the actual values of the constants at all, i.e. when you only care about them having some well-behaved unique values. In all other cases macros are a better choice (or basically the only choice).

当您拥有一组紧密关联的连续整数常量时,枚举最有效。当你一点都不关心常数的实际值时,它们就能很好地工作,也就是说,当你只关心它们有一些行为良好的唯一值时。在所有其他情况下,宏都是更好的选择(或者基本上是唯一的选择)。

#6


1  

As a practical matter, there is little difference. They are equally usable as constants in your programs. Some may prefer one or the other for stylistic reasons, but I can't think of any technical reason to prefer one over the other.

就实际情况而言,没有什么不同。它们与程序中的常量一样可用。有些人可能出于风格上的原因喜欢其中一个,但我想不出任何技术上的原因来喜欢另一个。

One difference is that macros allow you to control the integral type of related constants. But an enum will use an int.

一个区别是宏允许您控制相关常量的积分类型。但是enum将使用int形式。

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

printf("%ld\n", X);
printf("%d\n", Y); /* Y has int type */