【问题标题】:better way to check if variable has enum value in C检查变量是否在C中具有枚举值的更好方法
【发布时间】:2018-07-19 18:07:28
【问题描述】:

我的问题与代码的外观以及它在性能方面的表现有关。

假设我有一个枚举:

typedef enum {
   THIS_IS_A_VALUE1 = 65,
   THIS_IS_A_VALUE2 = 10,
   THIS_IS_A_VALUE3 = 45,
   THIS_IS_A_VALUE4 = 5,
   THIS_IS_A_VALUE5 = 7
} I_AM_AN_ENUM;

我想检查某个变量是否包含枚举中的值之一: 我想了三种方法:

1.

int val;
if(val != THIS_IS_A_VALUE1 && val != THIS_IS_A_VALUE2 && val != THIS_IS_A_VALUE3...)
{
// val is different than all values
// do something
}

2.

if(val != THIS_IS_A_VALUE1)
{
// keep blank
}
else if(val != THIS_IS_A_VALUE2)
{
// keep blank
}
else if(val != THIS_IS_A_VALUE3)
{
// keep blank
}....
else
{
// val is different than all values
// do something
}

3.

switch(val)
{
case THIS_IS_A_VALUE1:
case THIS_IS_A_VALUE2:
case THIS_IS_A_VALUE3:
..
..
default:
// val is different than all values
// do something
}

说实话,我觉得 2 和 3 很难看,尤其是当枚举中有很多值时。 第一种方法也可能很丑陋,但比其他方法更好,尽管根据我的理解使用大量 && 会降低性能。

总结一下:可读性和性能对我来说非常重要, 我真的不确定哪个选项是最好的, 或者也许还有其他方法可以做到这一点?

谢谢。

编辑:我忘了提到这些值是不是连续的,就是这样,我无法真正改变它。

【问题讨论】:

  • 对我来说听起来像是 XY 问题。你为什么要这样做?
  • 如果需要扩展,使用hashmap查找。 stackoverflow.com/questions/838404/implementing-a-hashmap
  • 为什么你有一个带有随机值的 ENUM,而不是序列号?如果它们是连续的,您只需检查范围即可。
  • 从您的代码中,在我看来,您想查看枚举是否真的在 外部 任何定义的值 - 这对我来说似乎是一个不同的问题。
  • @Barmar 在某些软件技术领域中,除了顺序范围之外,还很常见。嵌入式浮现在脑海中。

标签: c performance enums code-readability


【解决方案1】:

枚举本质上与使用宏定义常量相同,只是枚举将一组关联的常量包装成一种数据类型。这使您的代码更具自我记录性,但并没有真正提供任何额外的功能。


以下是您的问题出现的两种情况。

案例1:枚举中的值不是顺序的,意味着

enum Foo { FIRST=0, MIDDLE=40, LAST=12 };

bool isInFoo(enum Foo x){
bool found = false;
switch(x){
   case FIRST: found = true;  break;
   case MIDDLE: found = true; break;
   case LAST: found = true; break;
   }
return found;
}

这样,如果你不处理 switch 内部的所有枚举,你会得到一个编译时警告。

案例 2:值是顺序的。然后检查enum Bar x是否在枚举中,你可以这样做

enum Bar { FIRST=10, MIDDLE=11, LAST=12 };
bool isInBar(enum Bar x){ return x >= FIRST && x <= LAST; }

编辑:根据下面的 cmets,您可以将 Dummy Last 条目添加到 BarOUT_OF_RANGE = LAST+1,然后在函数 isInBar() 中,您可以检查 x 是否小于 OUT_OF_RANGE,这如果我们向枚举添加更多元素,我们将不必更改 isInBar() 函数,因为新添加的值应该小于 OUT_OF_RANGE。最终还是看你是否包含。


请注意,在情况 2 中,枚举值不一定需要是连续的序列号,您可以将它们保持在您希望的任何序列中,但是您需要相应地更改您的 isInBar()

enum FooBar { FIRST=3, SECOND=6, THIRD=9, FOURTH=15, LAST=12 };

bool isInFooBar(enum FooBar x){
bool found = false;
   for(int t=3;t<=15;t+=3)
      if(x == t) {
         found = true;
         break;
      }
return found;
}

【讨论】:

  • 是的,这也可以,OP 写 int x,所以我也是这样写的。不用担心我会改变它
  • 如果您在第一个和最后一个值中添加替代名称,例如 BeginEnd,则会获得奖励积分。
  • 案例2可以处理好一点。 enum Bar { FIRST=10, MIDDLE, LAST, OUT_OF_RANGE }。然后使用&amp;&amp; x &lt; OUT_OF_RANGE。这允许您通过将新元素放在 OUT_OF_RANGE 之前将它们添加到枚举中,并且您不必更改验证函数。
【解决方案2】:

我认为没有第四种方式会更好。我确实认为选项 3 是最好的选择,因为这样编译器可以检查您是否处理了所有情况。

当您决定向枚举添加选项时,这非常有用。然后编译器会给你一个警告,这样你就不会忘记包含新的案例。

【讨论】:

  • 除非您想查看 范围 - 然后切换会变得很尴尬。
【解决方案3】:

您可能希望封装检查逻辑并使用特殊名称来执行范围检查。采用这种方法提供了一些非常好的集合逻辑操作,比如检查这组值是否在这个集合中,或者检查它是否在这个集合中与另一个集合相交等等。

尽管我在 C++ 文件中使用 Visual Studio 2005 进行了编译,但我还没有测试过任何这些,它似乎可以使用它进行编译。换句话说,您可能需要对此进行一些调整。

typedef enum {
   THIS_IS_A_VALUE1 = 65,
   THIS_IS_A_VALUE2 = 10,
   THIS_IS_A_VALUE3 = 45,
   THIS_IS_A_VALUE4 = 5,
   THIS_IS_A_VALUE5 = 7
} I_AM_AN_ENUM;

typedef enum {
    IS_A_THIS_IS_A_VALUE1 = 0x01,
    IS_A_THIS_IS_A_VALUE2 = 0x02,
    IS_A_THIS_IS_A_VALUE3 = 0x04,
    IS_A_THIS_IS_A_VALUE4 = 0x08,
    IS_A_THIS_IS_A_VALUE5 = 0x10,
    IS_A_THIS_IS_VALUE123 = 0x07,       // bitwise Or of first three masks.
    IS_A_THIS_IS_VALUE_ANY = 0x1f       // is any of the values.
} IS_A_I_AM_ENUM;

struct {
    I_AM_AN_ENUM eVal;
    ULONG        ulMask;
} testArray[] = {
   {THIS_IS_A_VALUE1, IS_A_THIS_IS_A_VALUE1},
   {THIS_IS_A_VALUE2, IS_A_THIS_IS_A_VALUE2},
   {THIS_IS_A_VALUE3, IS_A_THIS_IS_A_VALUE3},
   {THIS_IS_A_VALUE4, IS_A_THIS_IS_A_VALUE4},
   {THIS_IS_A_VALUE5, IS_A_THIS_IS_A_VALUE5}
};

bool isInBar (I_AM_AN_ENUM x, ULONG ulCheckMask)
{
    int i;
    for (i = 0; i < sizeof(testArray)/sizeof(testArray[0]); i++) {
        if (testArray[i].eVal == x) {
           if (testArray[i].ulMask & ulCheckMask) return true;
           else break;
        }
    }
    return false;
}

// check a value against a range.
I_AM_AN_ENUM eVal = THIS_IS_A_VALUE3;

// check if eVal is Value3 or a Value 4
if (isInBar (eVal, IS_A_THIS_IS_A_VALUE3 | IS_A_THIS_IS_A_VALUE4)) // do something
if (isInBar (eVal, IS_A_THIS_IS_VALUE123 & ~IS_A_THIS_IS_A_VALUE1)) // do something
if (isInBar (eVal, IS_A_THIS_IS_VALUE_ANY))  // do something.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-12
    • 2013-03-28
    相关资源
    最近更新 更多