如何检查枚举变量是否有效?

15

我有一个枚举:

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++实现中,枚举值都是按递增的数字顺序分配的。所以你只需要在你的枚举中添加firstEnum = typeA, lastEnum = typeC,然后使用if(int(inputEnum) < int(firstEnum) || int(inputEnum) > int(lastEnum)) { /* 处理错误 */ }进行范围检查。 - Mike DeSimone
7个回答

23

通常的做法是这样的:

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#参考文档似乎没有提到它;他们的示例在右括号之后都没有任何内容。 - Patrick M
@PatrickM 在 C 中需要,但在 C++ 或 C# 中不需要,因为在 C 中无法将枚举标签用作类型。然而,为了使上面的答案有意义,我希望在 enum 关键字之前看到 typedef 关键字,声明 myenum_tenum myenum 相同类型。 - j b
如果该枚举是公共的,那么你就将 num_types 暴露给了全世界。有没有什么解决办法? - duhanebel
1
@duhanebel:好吧,它需要被公开,以便可以用于错误检查(毕竟这是问题的重点),并且还有助于定义数组大小等。显然,您会使用除“num_types”之外的其他名称,即适合您特定的“枚举”定义的名称。 - Paul R
@PaulR 如果我说错了,请纠正我,但是我认为如果您只需要对接受枚举作为参数的函数的实现进行一些合理性检查,则无需将num_types公开到公共API。您所需要的只是一个enum.count(在c / objc上不可用)。 - duhanebel
2
@duhanebel:这取决于枚举类型的使用范围 - 如果它在公共头文件中定义并在多个API中使用,则组成这些API的所有模块都可能希望使用num_types进行合理性检查、验证等。此外,还有我的另一个观点,即它非常适用于定义数组大小等,例如,数组维度需要对应于枚举类型中的类型数量。 - Paul R

6

您可以使用位掩码枚举:

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");

当然,这是假定你没有为枚举设置手动值的情况下,这种情况非常罕见,除非使用位枚举。

4
“但是,随着myenum_t的值越来越多,1 << n不会溢出吗?” - Luchian Grigore
@LuchianGrigore 有点像。你必须将变量名添加到或语句中,但如果你忘记这样做,那么不行。这只是一种更简洁的检查它是否属于三个值集合的方法。是的,它可能会溢出,这取决于你正在开发的机器上的整数大小。然而,在我的经验中,很少需要处理超过32个不同值的枚举,这是最大值的一个很好的估计。 - Richard J. Ross III
1
似乎有点过度,但这只是我的看法。 - Luchian Grigore
1
问题是,你不能依赖这个。它会导致未定义行为。所以你可能运气不好,在你的配置上它能工作,但是当交付给客户时,它会崩溃。我建议避免使用这个。 - Luchian Grigore

4

是的。

让编译器自己完成工作,不要将int强制转换为enum类型,那么你就可以了。


1
但有时我们需要进行类型转换。 - Ivan Kush

1

很遗憾,在语言层面上(至少在C中)没有简单的方法来实现它,您只需确保仅使用通过enum定义的变量。

虽然您可以启用以下编译器警告之一,并与-Werror一起使用:

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

如果在switch中缺少其中一个枚举,则会导致构建失败。


1

我过去使用的一个技巧:

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

然后检查您的值是否为> FIRST_FOO && < LAST_FOO1

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

否则,在C语言中,没有好的方法可以实现您所要求的功能。


1来自最新的在线C语言标准

6.7.2.2 枚举说明符
...
3 枚举列表中的标识符被声明为具有类型 int 的常量,并可以出现在任何允许使用此类常量的地方。109) 带有 = 的枚举器将其枚举常量定义为常量表达式的值。如果第一个枚举器没有 =,则其枚举常量的值为 0。每个后续没有 = 的枚举器将其枚举常量定义为通过将前一个枚举常量的值加 1 而获得的常量表达式的值。(使用带有 = 的枚举器可能会产生具有与同一枚举中其他值重复的值的枚举常量。)枚举的枚举器也称为其成员。

0

你不能也做一些类似的事情吗?

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 < LastType 不应该被优化掉,同样可能需要一个 FirstType - dashesy

0
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: 错误:初始化参数1时出错‘void f(E)’

因此,您可以看到枚举成员已经检查过了。

如果您想要更强的类型检查,并且具有C++11兼容的编译器,则可以为枚举使用更强的类型检查,请参见http://en.wikipedia.org/wiki/C%2B%2B11#Strongly_typed_enumerations


1
@LightnessRacesinOrbit 我真的不知道它应该是什么,因为四年后我的记忆有些模糊了... :) 它可能应该是一个整数字面量。 - Some programmer dude

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接