在C中使用枚举而不是#defines用于编译时常量是否合理?

ein*_*ica 18 c enums constants c-preprocessor

在使用C++一段时间后,我回到了一些C开发阶段.我已经明白,如果没有必要支持让编译器在编译时为你做更多工作,应该避免使用宏.因此,对于常量值,在C++中我会使用静态const变量,或者使用C++ 11枚举类来实现更好的范围.在C中,静态常量不是真正的编译时常量,枚举可能(?或可能不?)的行为略有不同.

那么,更喜欢使用枚举作为常量而不是#defines是否合理?

作为参考,这里有一个很好的列表,包括枚举,#define和C++中的静态consts.

Bas*_*tch 7

使用enum { FOO=34 };over 的优点#define FOO 34是宏被预处理,所以原则上编译器并没有真正看到它们(实际上,编译器确实看到了它们;最近的GCC有一个复杂的基础结构来从宏扩展中给出一些内部抽象语法树是未来).

特别是,调试器更容易了解FOOenum { FOO=34 };不是来自#define FOO 34(但同样,在实践中并不总是如此;有时,调试器足够聪明,能够扩展宏......).

正因为如此,我更喜欢 enum { FOO=34 };#define FOO 34

而且还有打字优势.我可以使用enum color_en { WHITE, BLACK }; enum color_en color;比使用编译器更多的警告bool isblack;

BTW static const int FOO=37;通常由调试器知道,但编译器可能会对其进行优化(因此没有使用内存位置;它可能只是机器代码中某些指令内的一个立即操作数).


Yve*_*ust 7

我会坚持使用这些功能用于他们的目的.

在一组备选方案中采用离散值的符号参数应表示为枚举成员.

数值参数(如数组大小或数字容差)应表示为const变量.不幸的是,C没有适当的构造来声明编译时常量(就像Pascal那样),我倾向于说定义的符号同样可以接受.我现在甚至非正统地选择使用与其他标识符相同的套管方案的定义符号.

具有显式指定值的枚举(例如二进制掩码)更加有趣.冒着挑剔的风险,我会考虑使用声明的常量,比如

#define IdleMask 1
#define WaitingMask 2
#define BusyMask (IdleMask | WaitingMask)
enum Modes { Idle= IdleMask, Waiting= WaitingMask, Busy= BusyMask };
Run Code Online (Sandbox Code Playgroud)

这就是说,当你看到他们如何轻松地处理他们每天收到的巨大代码时,我不会太在意放宽编译器的任务.

  • `BusyMask`是等待发生的事故.始终用括号括起非平凡的预处理器常量. (4认同)
  • 另外,我会使用`IdleMask 0x1 << 0`和`WaitingMask 0x1 << 1`(或者至少是'0x01`和`0x02`)来强调这些的按位读取. (3认同)
  • 我认为在使用之后`#undef`临时宏是很好的做法.特别是如果那是在标题中.然而,这甚至会进一步膨胀这个冗长的初始化,所以不知怎的,我没有看到这种情况...... (3认同)

小智 7

我在嵌入式系统领域工作了十几年,主要使用 C 语言。我的评论是针对这个领域的。可通过三种方法来创建对这些类型的应用程序具有特定含义的常量。

1) #define:宏在代码提交给 C 编译器之前由 C 预处理器解析。当您查看处理器供应商提供的头文件时,他们通常有数千个定义对处理器寄存器的访问的宏。您在代码中调用它们的子集,它们将成为 C 源代码中的内存访问。其余的消失并且不会呈现给 C 编译器。

定义为宏的值在 C 中变成文字。因此,它们不会导致任何数据存储。没有与定义相关的数据存储位置。

宏可用于条件编译。如果您想根据功能配置删除代码,那么您必须使用宏定义。例如:

#if HEARTBEAT_TIMER_MS > 0
    StartHeartBeatTimer(HEARTBEAT_TIMER_MS);
#endif
Run Code Online (Sandbox Code Playgroud)

2)枚​​举:与宏定义一样,枚举不会导致数据存储。它们变成了文字。与宏定义不同,它们不会被预处理器删除。它们是 C 语言结构,将出现在预处理的源代码中。它们不能用于通过条件编译来剥离代码。无法在编译时或运行时测试它们是否存在。值只能作为文字参与运行时条件。

未引用的枚举在编译的代码中根本不存在。另一方面,如果未在 switch 语句中处理枚举值,编译器可能会发出警告。如果常量的目的是产生一个必须进行逻辑处理的值,那么只有枚举才能提供使用 switch 语句所带来的安全程度。

枚举还具有自动递增功能,因此如果常量的目的是用作数组的常量索引,那么我将始终使用枚举来避免未使用的槽。事实上,枚举本身可以生成一个常量,表示可以在数组声明中使用的多个项目。

由于枚举是 C 语言结构,因此它们肯定会在编译时评估。例如:

#define CONFIG_BIT_POS 0
#define CONFIG_BIT_MASK (1 << CONFIG_BIT_POS)
Run Code Online (Sandbox Code Playgroud)

CONFIG_BIT_MASK 是 (1 << CONFIG_BIT_POS) 的文本替代。当 (1 << CONFIG_BIT_POS) 被提供给 C 编译器时,它可能会也可能不会产生文字 1。

enum {
    CONFIG_BIT_POS = 0,
    CONFIG_BIT_MASK = (1 << CONFIG_BIT_POS)
};
Run Code Online (Sandbox Code Playgroud)

在这种情况下,CONFIG_BIT_MASK 被评估并变为文字值 1。

最后,我想补充一点,宏定义可以组合起来产生其他代码符号,但不能用于创建其他宏定义。这意味着,如果必须派生常量名称,那么它只能是由宏符号或宏扩展组合创建的枚举,例如列表宏(X 宏)。

3) const:这是一个 C 语言结构,使数据值只读。在嵌入式应用中,当应用于静态或全局数据时,这具有重要作用:它将数据从 RAM 移动到 ROM(通常是闪存)。(它对局部变量或自动变量没有这种影响,因为它们是在运行时在堆栈或寄存器中创建的。)C 编译器可以对其进行优化,但当然可以防止这种情况,因此除了这个警告之外,const 数据实际上需要运行时在只读存储器中存储。这意味着它具有类型,该类型定义了已知位置的存储。它可以是 sizeof() 的参数。它可以在运行时由外部应用程序或调试器读取。

这些评论针对嵌入式应用程序。显然,对于桌面应用程序,所有内容都在 RAM 中,其中大部分内容并不真正适用。在这种情况下,const 更有意义。


BЈо*_*вић 4

更喜欢使用枚举作为常量而不是 #define 是否合理?

如果你喜欢。枚举的行为类似于整数。

但我仍然更喜欢常量,而不是枚举和宏。常量提供类型安全,并且它们可以是任何类型。枚举只能是整数,而宏不考虑类型安全。

例如 :

const int MY_CONSTANT = 7;
Run Code Online (Sandbox Code Playgroud)

代替

#define MY_CONSTANT 7
Run Code Online (Sandbox Code Playgroud)

或者

enum
{
  MY_CONSTANT = 7
};
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我的回答与 C++ 有关。我不确定它是否适用于 C。