假设我在整个程序中经常使用一些字符串(用来存储状态之类的)。字符串操作可能很昂贵,所以无论何时处理它们,我都喜欢使用枚举。到目前为止,我已经看到了一些解决方案:
typedef enum {
STRING_HELLO = 0,
STRING_WORLD
} string_enum_type;
// Must be in sync with string_enum_type
const char *string_enumerations[] = {
"Hello",
"World"
}
我经常遇到的另一个:
typedef enum {
STRING_HELLO,
STRING_WORLD
} string_enum_type;
const char *string_enumerations[] = {
[STRING_HELLO] = "Hello",
[STRING_WORLD] = "World"
}
这两种方法的优缺点是什么?有没有更好的?
前者的唯一优点是它向后兼容古老的C标准。
除此之外,后一种选择更好,因为即使枚举被修改或项目更改位置,它也能确保数据完整性。但是,它应该通过检查来完成,以确保枚举中的项目数量与查找表中的项目数量相对应:
typedef enum {
STRING_HELLO,
STRING_WORLD,
STRING_N // counter
} string_enum_type;
const char *string_enumerations[] = {
[STRING_HELLO] = "Hello",
[STRING_WORLD] = "World"
};
_Static_assert(sizeof string_enumerations/sizeof *string_enumerations == STRING_N,
"string_enum_type does not match string_enumerations");
以上是简单“枚举-查找表”耦合的最佳方法。另一种选择是使用结构,但这更适合更复杂的数据类型。
最后,作为附带说明,第三个版本将使用“X宏”。除非您对代码重复和维护有特殊要求,否则不推荐使用。为了完整起见,我将在此处包含它,但我不推荐在一般情况下使用它:
#define STRING_LIST \
/* index str */ \
X(STRING_HELLO, "Hello") \
X(STRING_WORLD, "World")
typedef enum {
#define X(index, str) index,
STRING_LIST
#undef X
STRING_N // counter
} string_enum_type;
const char *string_enumerations[] = {
#define X(index, str) [index] = str,
STRING_LIST
#undef X
};
_Static_assert(sizeof string_enumerations/sizeof *string_enumerations == STRING_N,
"string_enum_type does not match string_enumerations");
另一种可能是使用函数,而不是数组:
const char *enumtostring(string_enum_type e) {
switch(e) {
case STRING_HELLO: return "hello";
case STRING_WORLD: return "world";
}
}
如果您添加了一个enum值,但忘记添加匹配的switch case,gcc至少会发出警告。
(我想您也可以尝试使这种函数inline
。)
附录:我提到的gcc警告仅适用于< code>switch语句没有< code>default case的情况。因此,如果您想打印一些不知何故悄悄通过的越界值,您可以这样做,不是用< code>default的情况,而是像这样:
const char *enumtostring(string_enum_type e) {
switch(e) {
case STRING_HELLO: return "hello";
case STRING_WORLD: return "world";
}
return "(unrecognized string_enum_type value)";
}
包含越界值也很好:
static char tmpbuf[50];
snprintf(tmpbuf, sizeof(tmpbuf), "(unrecognized string_enum_type value %d)", e);
return tmpbuf;
(最后一个片段有一些额外的限制,但是这个附录已经很长了,所以我现在不想再赘述了。)
另一种可能是用户#定义
。
尽管使用它有很多缺点,但主要的好处是#定义
不占用空间,除非使用它们。。。
#define STRING_HELLO "Hello"
#define STRING_WORLD "World"