【问题标题】:Iterate through enums in C?遍历C中的枚举?
【发布时间】:2017-03-23 10:02:50
【问题描述】:

假设我有几个在枚举中定义的硬件寄存器:

typedef enum registers
{
    REG1 = 0,
    REG2 = 1,
    REG3 = 2,
    REG4 = 4,
    REG5 = 6,
    REG6 = 8,
    
    REG_MAX,
    
}

我对这些寄存器有默认值(其实还没决定用哪种方式定义它们,#define、数组还是枚举...):

// This is just conceptual, not really an enum, array or #define :)
typedef enum values
{
    VALUE_REG1 = A,
    VALUE_REG2 = B,
    VALUE_REG3 = C,
    VALUE_REG4 = 53,
    VALUE_REG5 = 88,
    VALUE_REG6 = 24,
    
    MAX_NUM_REG_VALUES    
}

我有一个可以读取硬件寄存器的函数:

uint8 readRegs(uint8 regaddr);

现在我想遍历registers,并在每个元素上调用readRegs() 函数并将其与枚举regvalues 进行比较。它看起来像:

registers regs;
reg_values values;
uint8 readfromregs;

for (regs = REG1; regs <= REG_MAX; regs++)
{
    readfromregs = readRegs(regs);
    for (values = VALUE_REG1; reg_values <= MAX_NUM_REG_VALUES; reg_values++)
    {
        if (readfromregs != values)
        {
             // Value not correct
        }
        else
        {
             // value correct
        }
    }
}

这不起作用,因为无法以这种方式迭代枚举。有人有更好的解决方案吗?如何定义构造enum reg_values枚举寄存器必须是固定的(这不能改变数组)。

【问题讨论】:

  • MAX_NUM_REG_VALUES 是 25,所以你知道。
  • 当然是一个数组。您可以定义对或结构的数组(例如,pair)。
  • @KonstantinL 这看起来很像 C++,而不是 C。但是,是的,一些非 C++ 数据类型的数组。
  • 对不起。好吧,那么只使用一个结构数组: struct CRegister { int register;整数默认值; } registerArray[100];
  • REG_MAX 技巧只应在枚举数相邻时使用,此处并非如此。

标签: c enums


【解决方案1】:

我可能会定义一个结构

struct RegInfo
{
    registers number;
    values defaultValue;
}

然后我将创建一个将寄存器编号与默认值匹配的数组

struct RegInfo registerInfo[] =
{
    { REG1, VALUE_REG1 },
    { REG2, VALUE_REG2 },
    { REG3, VALUE_REG3 },
    { REG4, VALUE_REG4 },
    { REG5, VALUE_REG5 },
    { REG6, VALUE_REG6 },
};

现在迭代寄存器:

for (int i = 0 ; i < sizeof registerInfo / sizeof(RegInfo) ; ++i)
{
    values value = readFromReg( registerInfo[i].number);
    if (value != registerInfo[i].defaultValue)
    {
         // Not the default
    }
}

如果你想对每个值使用一个内循环,除了数组可以直接使用值之外的相同技巧

values allValues[] = { VALUE_REG1, Value_REG2, ... , VALUE_REG6 };

您可能会忘记将新值/寄存器放入相关数组中,但这就是单元测试的目的。

【讨论】:

  • 如果你担心在维护过程中搞乱数组初始化,你可以做struct RegInfo registerInfo[] = { [0] = { REG1, VALUE_REG1 }, [1] = { REG2, VALUE_REG2 }, ... }。如果以后有人来更改该列表,他们将很难搞砸。
  • 另外,一个优化的挑剔是这样的结构提供的缓存性能比保留一个单独的枚举数组和一个带有值的数组更差。我想说代码的可读性更重要,但要牢记这一点。
  • @Lundin 我更多地考虑是否有人添加了新的枚举值并忘记将其添加到数组中。
  • 我发布了关于如何解决该问题的补充答案。
【解决方案2】:

REG_MAX 将是 9MAX_NUM_REG_VALUES 将是 25 所以你不能使用它们。您必须以不同的方式枚举常量。

根据@JeremyP 的另一个答案中的结构解决方案,您可以将第三个枚举用于实际索引:

typedef enum
{
  REG_INDEX1 = REG1,
  REG_INDEX2 = REG2,
  REG_INDEX3 = REG3,
  REG_INDEX4 = REG4,
  REG_INDEX5 = REG5,
  REG_INDEX6 = REG6,
  REG_INDEX_N // number of registers
} reg_index;

这提供了一些提高数据完整性的方法:

struct RegInfo registerInfo[] =
{
  [REG_INDEX1] = { REG1, VALUE_REG1 },
  [REG_INDEX2] = { REG2, VALUE_REG2 },
  [REG_INDEX3] = { REG3, VALUE_REG3 },
  [REG_INDEX4] = { REG4, VALUE_REG4 },
  [REG_INDEX5] = { REG5, VALUE_REG5 },
  [REG_INDEX6] = { REG6, VALUE_REG6 },
};

_Static_assert(sizeof(registerInfo)/sizeof(*registerInfo) == REG_INDEX_N, 
               "Enum not in sync with array initializer");

这是我推荐的解决方案。


如果您对数据完整性非常迂腐,并且希望避免代码重复,那么您还可以使用一些邪恶的宏魔法。即以严重降低可读性为代价的“X 宏”:

// whatever A, B and C is supposed to be
#define A 10
#define B 11
#define C 12

#define REG_LIST \
  X(0, A)        \
  X(1, B)        \
  X(2, C)        \
  X(4, 53)       \
  X(6, 88)       \
  X(8, 24)       \

int main (void)
{
  // iterate through the value pairs:
  #define X(reg, val) printf("reg: %d val:%.2d\n", reg, val);
  REG_LIST
  #undef X
}

同样,您可以使用 X 宏来创建枚举:

typedef enum // create REG1, REG2, REG4 etc
{
  #define X(key, val) REG##key = key,
  REG_LIST
  #undef X
} registers;

typedef enum // create VALUE_REG1, VALUE_REG2, VALUE_REG4 etc
{
  #define X(key, val) VALUE_REG##key = val, 
  REG_LIST
  #undef X
} values;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 2010-12-12
    • 2011-09-01
    • 2023-03-03
    • 2015-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多