【问题标题】:#define or enum? [duplicate]#define 还是枚举? [复制]
【发布时间】:2010-06-28 17:42:05
【问题描述】:

可能重复:
Why use enum when #define is just as efficient?

在 C 中编程时,使用#define 语句或枚举来表示状态机中的状态是更好的做法吗?

【问题讨论】:

  • 回答为“更喜欢#define”的问题数量少得惊人。
  • @Stephen:主要是因为很多发帖者都被锁定在C++模式,而问题是关于C的。C和C++在这方面的区别是相当大的。事实上,在 C 中大多数情况下你应该更喜欢#define,但在这种特定情况下(状态机)enum 确实是更好的方法。
  • 据我记忆,重复的东西。也许规范“状态机中的状态”,改变了一点,但没有理由认为“状态机的状态”是不同的#define 或 enums 可用于什么的应用程序...
  • @AndreyT :同意 C++ 进一步减少了对 #define 的需求,但由于 C90 const 已成为公认的关键字......杀死了 99% 的使用。这种情况(正如你提到的)最好使用enum。使用预处理器符号会使调试变得更加困难。显然,macros_require #define。所以,除了在某些情况下可能比编译器更聪明地连接符号之外,您对 c 中的 #define 常量(自 C90 起)有什么好处? (我应该补充一点,也许是为了证明你的观点,我在 C++ 中工作)
  • @Stephen: C 中的const 对此毫无用处。这实际上就是我所说的差异。在 C 中 const 不会产生 constant,但在 C++ 中会。在 C 中 const 创建一个“const 对象”,它不是常量,不能在需要常量的地方使用。这就是为什么在 C 中你的选择仅限于 #defineenum

标签: c


【解决方案1】:

技术上没关系。编译器很可能会为这两种情况创建相同的机器代码,但枚举具有三个优点:

  1. 使用正确的编译器+调试器组合,调试器将按枚举名称而不是编号打印枚举变量。所以“StateBlahBlup”读起来比“41”好很多,不是吗?

  2. 您没有明确地给每个状态一个编号,如果您允许编译器会为您编号。假设您已经有 20 个状态,并且您想在中间添加一个新状态,在定义的情况下,您必须自己进行所有重新编号。在枚举的情况下,您只需添加状态,编译器将为您重新编号此新状态下的所有状态。

  3. 如果 switch 语句没有处理所有可能的枚举值,你可以告诉编译器警告你,例如因为您忘记处理某些值,或者因为枚举已扩展但您忘记更新处理枚举值的 switch 语句(但如果存在 default 情况,它不会发出警告,因为所有未明确处理的值都以默认值结束案例)。

【讨论】:

  • 还有 4. 与 #define 不同,枚举值在符号信息中表示,这意味着像 GDB 之类的东西可以在表达式中显示/使用它(这与第 1 点有关)。 5. 你可以#undef #define 然后创建另一个,所以它不像枚举那样不可变。
  • @AndrewBainbridge 不变性是我迄今为止从未考虑过的一个好点。我很少使用#undef,以至于我几乎忘记了它的存在,但你是对的,你可以改变define的值。
  • Enums 还可以帮助 IDE 自动完成,有些会为您生成一个包含所有枚举案例的完整 switch 语句。
  • 哦,当然还有自动生成枚举字符串转换,通过单独的工具,甚至像这个 C++ 库那样的代码内:github.com/Neargye/magic_enum
【解决方案2】:

由于状态是相关的元素,我认为最好有一个枚举来定义它们。

【讨论】:

    【解决方案3】:

    没有确定的答案。 enum 为您提供范围和自动赋值,但不提供对常量类型的任何控制(始终为 signed int)。 #define 忽略范围,但允许您使用更好的类型工具:允许您选择常量类型(通过使用后缀或通过在定义中包含显式转换)。

    所以,为自己选择对你来说更重要的东西。对于状态机,enum 可能是更好的选择,除非您有充分的理由来控制类型。

    【讨论】:

    • 大多数编译器允许你指定枚举的大小。
    • @Adam Shiemke: ...这通常是一个全局设置。我说的是类型,而不是大小。 C 和 C++(以其当前形式)都没有为您提供对枚举值类型的任何有意义的控制。
    • AFAIR C99 没有为您提供enum 常量类型的选择,即它将它们固定为signed int 类型的常量。 enum 类型本身可以有不同的宽度,编译器可以选择一个方便的大小,以适应该类型定义的所有常量。因此,特别是您的尺寸可能小于int,但通常不会更大。
    • @Jens Gustedt:当我提到打字时,我主要是在谈论这些值在表达式中的类型,而不是这些对象占用的内存量。在表达式中,枚举常量被提升为int 或更大。例如,您不能告诉编译器某个枚举常量应该充当unsigned int - 这是枚举的问题,可以通过#define 解决。
    • 是的。如果您从一个枚举隐式转换为另一种或任何其他类型,我相信 gcc 会生成警告。我相信 CodeWarrior 会的。但总的来说你是对的。
    【解决方案4】:

    我更喜欢枚举。它们更紧凑,更“安全”。您还可以在枚举中暗示顺序,这在状态机中可能会有所帮助。如果可能,应避免使用 #define,因为它们会覆盖源代码中的所有出现,这可能会导致一些难以调试的意外操作。

    【讨论】:

      【解决方案5】:

      如果您的编译器支持enum,那将是首选。如果做不到这一点,请使用#define。所有 C++ 编译器和现代 C 编译器都应该支持enum,但较旧的编译器(尤其是针对嵌入式平台的编译器)可能不支持enum

      如果您必须使用#define,请务必使用括号定义常量,以避免预处理器错误:

      #define RED_STATE    (1)
      #define YELLOW_STATE (2)
      #define GREEN_STATE  (3)
      

      【讨论】:

        【解决方案6】:

        #define 指令可能会产生许多意想不到的后果,并且不遵循常见的范围规则。当你有相关数据时使用枚举。

        更多信息:http://www.embedded.com/columns/programmingpointers/9900402?_requestid=341945 [C++ 材料,但仍然有些相关]

        【讨论】:

        • 您链接的信息是关于 C++,而不是 C。在选择定义常量的方法时,C 和 C++ 之间的区别是 巨大
        • 无论如何,如果可能,最好使用枚举,因为它们是类型安全的并且不会覆盖任何其他代码
        • @Nathan Fellman:不。在 C 中,enum 仅在我们使用自然常量组时更可取。在所有其他情况下,它是#define,只有#define
        • @Andrey:很好,出于某种原因,我认为 C++ 是这里的预期平台。
        • @Andrey,你为什么要如此强烈地推荐#define? enum 有什么缺点?
        【解决方案7】:

        你可以用这个技巧让编译器检查#define值的类型。

        #define VALUE_NAME ((TYPE_NAME) 12)
        

        然而#define 的真正问题是它可以在应用程序代码中重新定义。 (当然编译器会警告你。)

        【讨论】:

          【解决方案8】:

          enum 在您有独占选项时很棒,但您不能使用它们来定义位域标志,如下所示:

          #define SQ_DEFAULT 0x0
          #define SQ_WITH_RED 0x1
          #define SQ_WITH_BLUE 0x2
          
          void paint_square(int flags);
          

          然后你可以画红蓝方块:

          paint_square(SQ_WITH_RED | SQ_WITH_BLUE);
          

          ...enum 无法做到这一点。

          【讨论】:

          • 是的,你可以:enum (a = 1, b = 2, c = 4, d = 5)
          • 不能吗?当然,您可以:enum { SQ_DEFAULT = 0x0, SQ_WITH_RED = 0x1, SQ_WITH_BLUE = 0x2 }。这里的问题是,使用标志最好使用 unsigned 类型。但否则,枚举将起作用。
          • 当然可以! enum Color { default = 0x0, red = 0x1, blue = 0x2 }; 及以后的paint_square(red | blue)
          • 好吧,我知道你可以像这样为枚举项设置值,但不是以这种方式使用它们……非传统?
          • @Joseph Quinsey:标准规定编译器可以使用任何类型来存储枚举对象,但标准非常明确地规定了它们必须在 value 中的行为方式上下文:C89/90 中的 6.5.2.2 明确指出枚举常量的类型为 int。 C99 说了同样的话。没有变体。 GCC 在这方面被打破了。
          【解决方案9】:

          你可以使用任何你想要和喜欢的东西。

          尽管每个人都在说,我也想把我加起来为 Enums 投票。

          如果您使用相关数据,例如状态机,则应始终首选枚举,您可以在枚举中定义顺序,这也将有助于实现状态机。

          更多的枚举将确保您的程序安全,因为所有枚举都只是它的类型,因此它们也将避免任何可能的混淆。

          #define 不应用于状态机或相关数据。无论如何,这是我的建议,但没有硬性规定。

          此外,我还想补充一点,如果将来使用或被其他人阅读,枚举将为您的代码增加更多的可读性和可理解性。当您有一个非常大的程序并且除了用于状态机之外的程序中有很多#defines 时,这一点很重要。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-08-12
            • 1970-01-01
            • 1970-01-01
            • 2010-11-23
            • 2023-04-10
            • 1970-01-01
            • 1970-01-01
            • 2021-06-29
            相关资源
            最近更新 更多