【问题标题】:How to check if an enum variable is valid?如何检查枚举变量是否有效?
【发布时间】:2012-02-06 14:44:12
【问题描述】:

我有一个枚举:

enum myenum{
  typeA,
  typeB,
  typeC
} myenum_t;

然后,将使用枚举参数调用函数:

int myfunction(myenum_t param1)
{
  switch(param1)
  {
    case typeA:
    case typeB:
    case typeC:
      //do the work
      break;

    default:
      printf("Invalid parameter");
  }
  return 0;
}

但是,随着myenum_t 的值越来越多,myfunction 似乎并不那么优雅了。

有没有更好的方法来检查枚举是否有效?

【问题讨论】:

  • 没有标准的副本,如果没有引用它,我会因为这样说而被撕毁,所以我会发表评论:在我见过的每个 C 或 C++ 实现中,enum 值以递增的数字顺序分配。所以您需要做的就是将firstEnum = typeA, lastEnum = typeC 添加到您的enum,然后使用if(int(inputEnum) < int(firstEnum) || int(inputEnum) > int(lastEnum)) { /* handle error */ } 进行范围检查。

标签: c enums


【解决方案1】:

对此的常见约定是执行以下操作:

typedef enum {
  typeA,
  typeB,
  typeC,
  num_types
} myenum_t;

然后你可以检查(t < num_types)

如果您随后添加更多枚举,例如

typedef enum {
  typeA,
  typeB,
  typeC,
  typeD,
  typeE,
  num_types
} myenum_t;

然后num_types 会自动更新,您的错误检查代码不需要更改。

【讨论】:

  • 在您的枚举声明之后,myenum_t 是什么意思? C# Reference 似乎没有提到它;他们的例子在右大括号后都没有。
  • @PatrickM 在 C 中是必需的,但在 C++ 或 C# 中不需要,因为在 C 中不能使用枚举标记作为类型。但是,为了使上述答案有意义,我希望在 enum 关键字之前看到 typedef 关键字,将 myenum_t 声明为与 enum myenum 相同的类型。
  • 如果该枚举是面向公众的,那么您将 num_types 暴露给世界。有什么办法吗?
  • @duhanebel:好吧,它需要暴露出来,以便它可以用于错误检查(毕竟问题的全部重点),它对于定义数组大小等也很有用. 显然,您将使用除 num_types 之外的名称,即适合您特定的 enum 定义的名称。
  • @duhanebel:这取决于枚举的使用范围——如果它在一个通用头文件中定义并在多个 API 中使用,那么构成这些 API 的所有模块都可能希望使用 num_types 进行完整性检查,验证等。还有一点我的补充是,它对于定义数组大小等非常有用,例如数组维度需要对应于枚举中的类型数。
【解决方案2】:

您可以使用按位枚举:

enum myEnum {
    typeA = 1 << 0;
    typeB = 1 << 1;
    typeC = 1 << 2;
}

int myFunction(myEnum arg1)
{
    int checkVal = typeA | typeB | typeC;

    if (checkVal & arg1)
    {
        // do work here;
    }
    else
    {
        printf("invalid argument!");
    }

    return 0;
}

对不起,我好像读错了问题。

看来您想要做的是确定您是否传入了正确的值,而不是一些随机的无效选项。在这种情况下,最合乎逻辑的选择是:

if (arg1 < typeA || arg1 > typeC)
    printf("invalid argument");

当然,这是假设您没有为枚举设置手动值,除非使用按位枚举,否则这种情况非常罕见。

【讨论】:

  • “但是,随着 myenum_t 的值越来越多”。 1 &lt;&lt; n不会有溢出的可能吗?
  • @LuchianGrigore 之类的。您必须将变量名称添加到 or 语句中,但如果您忘记这样做,则不会。这只是检查它是否是一组三个值的更简洁的方法。是的,它可能会溢出,具体取决于您正在开发的机器上的整数大小。但是,根据我的经验,很少需要在一个枚举中处理超过 32 个不同的值,这是对您可能拥有的最大值的一个很好的估计。
  • 似乎有点矫枉过正,但这只是我。
  • 问题是,你不能依赖这个。它导致UB。所以你可能不走运并且让它在你的配置上工作,但是当发送给客户时,它会崩溃。我会避免这种情况。
【解决方案3】:

是的。

让编译器完成它的工作,不要将int 转换为enum 类型,你应该很好。

【讨论】:

  • 但有时我们需要投射
【解决方案4】:

不幸的是,没有一种简单的方法可以做到语言级别(至少使用C),您只需要确保只使用通过enum 定义的变量。

尽管您可以将以下编译器警告之一与-Werror 一起启用:

  • -Wswitch
  • -Wswitch-default
  • -Wswitch-enum

如果开关中缺少其中一个枚举,这会使构建失败。

【讨论】:

    【解决方案5】:

    我过去使用过的一个技巧:

    enum foo {FIRST_FOO, BAR, BLETCH, BLURGA, BLAH, LAST_FOO};
    

    然后检查您的值是否为&gt; FIRST_FOO &amp;&amp; &lt; LAST_FOO1

    当然,这假设您的枚举值之间没有间隙。

    否则,不,没有什么好方法可以满足您的要求(至少在 C 中)。


    1 来自最新在线C Language Standard
    6.7.2.2 枚举说明符
    ...
    3 枚举器列表中的标识符被声明为类型为int 和 可能出现在任何允许的地方。109) 带有= 的枚举器定义其 枚举常量作为常量表达式的值。如果第一个枚举器有 没有=,它的枚举常量的值为0。没有= 的每个后续枚举数 将其枚举常量定义为由下式获得的常量表达式的值 将1 添加到前一个枚举常量的值上。 (使用枚举器 = 可能会生成枚举常量,其值与同一目录中的其他值重复 枚举。)枚举的枚举数也称为其成员。

    【讨论】:

      【解决方案6】:

      你不能也做类似的事情

      enum myEnum {typeA,typeB, typeC};
      
      int myFunction (myEnum arg1) {
          if (arg1 >= typeA && arg1 <= typeC) {
              // do work here
          } else {
              printf("invalid argument!");
          }
          return 0;
      }
      

      【讨论】:

      • 编译器是否有可能优化检查?如果添加了 LastType,则检查 arg1 &lt; LastType 不应被优化,同样可能需要 FirstType
      【解决方案7】:

      C++ 中的枚举已经拥有比 C 更强的类型。

      采取以下简单程序:

      #include <iostream>
      
      enum E
      {
          A,
          B 
      };
      
      void f(E e)
      {
      }
      
      int main()
      {
          f(1);
      }
      

      使用 GCC 编译器会出现以下错误:

      enum.cpp:在函数“int main()”中: enum.cpp:15:错误:从“int”到“E”的无效转换 enum.cpp:15:错误:初始化“void f(E)”的参数 1

      所以你可以看到枚举成员已经被检查了。

      如果您想要更强大的类型检查,并且拥有支持 C++11 的编译器,您可以对枚举使用更强大的类型检查,请参阅 http://en.wikipedia.org/wiki/C%2B%2B11#Strongly_typed_enumerations

      【讨论】:

      • @LightnessRacesinOrbit 我真的不知道它应该是什么,我的记忆在将近四年后有点模糊...... :) 可能应该是一个整数文字.
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-27
      • 2012-02-16
      • 1970-01-01
      • 2015-02-12
      相关资源
      最近更新 更多