c89 中的指示符
Designators in c89
C99 允许数组初始值设定项(以及其他)指定要使用正整数指示符设置数组的哪个元素($6.7.8.6、$6.7.8.17),例如:
const char *foo[] = {[2] = "foo", [1] = "bar", [0] = "baz"};
我以前用它来制作一个枚举到字符串 table,如下所示:
enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {
[THING_FOO] = "foo",
[THING_BAR] = "bar",
[THING_BAZ] = "baz"
}
但是,我现在的工作要求是我的代码符合 c89。
我研究了预处理器魔法(例如 here),但我需要字符串是任意的,而不是枚举符号的副本。
光做是不够的
enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {"foo", "bar", "baz"};
因为我以后需要添加枚举元素。使用 c99 方法,这将导致 table 中出现 NULL 指针,如果它们成为问题,则很容易调试。如果我忘记使用此方法更新字符串 table,我会得到更难调试的段错误。如果我无论如何都必须记住偏移量,它也会破坏具有符号的意义。
如果声明在函数中,我可以像这样达到预期的效果:
enum {THING_FOO = 0, THING_BAR, THING_BAZ, NUM_THINGS};
void foo(void)
{
static const char *table[NUM_THINGS];
table[THING_FOO] = "foo";
table[THING_BAR] = "bar";
table[THING_BAZ] = "baz";
/* ... */
}
但是,至少对于 gcc
,这 不会 得到优化。
有没有办法在c89中声明这样一个字符串table?
(组装没问题。)
简单老套的怎么样
const char* table[] = { "foo", "bar", "baz" };
换句话说,只要按正确的顺序排列即可。
char *foo_string = table[FOO];
当然,这只适用于像上面这样的简单枚举,不适用于样式中的枚举
enum { FOO = 13; BAR = 15, BAZ = 312 };
但为此,您必须创建一个至少包含 313 个元素的数组,其中大部分元素无论如何都是 NULL,这将是一个非常浪费的构造。在这种情况下,当您使用 switch
结构时,编译器可以为您优化。
也看看S.O。 @Leandros 指出的问题:How to convert enum names to string in c。那里的答案使用宏来生成数组,确保条目的顺序正确。
或者,正如那个答案所说:
#define enum_str(s) #s
这完全摆脱了数组。
#define DEF_FOO_ENUM(E0, S0, E1, S1, E2, S2) \
enum foo { E0, E1, E2 }; \
const char *foo_str = { S0, S1, S2 };
DEF_FOO_ENUM(THING_FOO, "foo",
THING_BAR, "bar",
THING_BAZ, "baz");
符号和字符串是成对的。您不会轻易添加没有字符串的新符号,反之亦然。要添加一个元素,您必须向宏添加两个新参数——E3, S3
——等等。那里没有什么可以保持同步的,只是 enum
有所有的 E
-s 而数组有所有的 S
-s。这几乎是不可能搞砸的。
在尝试了几种不同的技术之后,这个最容易维护:
const char *table[] = {
#define FOO 0
"foo",
#define BAR (FOO + 1)
"bar",
#define BAZ (BAR + 1)
"baz"
}
这里聚集了一个条目的所有信息。要插入一个元素,你只需要修改它周围的东西。例如插入 qux
:
const char *table[] = {
#define FOO 0
"foo",
#define QUX (FOO + 1) /* new */
"qux", /* new */
#define BAR (QUX + 1) /* modified */
"bar",
#define BAZ (BAR + 1)
"baz"
}
它有点丑(有点可爱,你知道吗?)但效果很好。
您可以使用 X-Macros 将它们放在一起:
#define MYXMACRO(OP) \
OP(ENUM_FOO, "foo") \
OP(ENUM_BAR, " bar") \
OP(ENUM_BAZ, "baz")
/* use the first parameter to set up your enums*/
enum {
#define AS_ENUMS(x,y) x,
MYXMACRO(AS_ENUMS)
#undef AS_ENUMS /*not required, just playing nice*/
NUMTHINGS
};
/* use the 2nd parameter to set up your strings*/
const char *strings[] = {
#define AS_STRINGS(x,y) y,
MYXMACRO(AS_STRINGS)
#undef AS_STRINGS
};
#undef MYXMACRO
现在您的新数据可以作为一组枚举和字符串添加。如果您稍后决定根据枚举添加其他内容,则可以使用 'z' 参数轻松扩展到 OP() 甚至 ...
和 __VA_ARGS__
用于多个但数量不同的参数。
C99 允许数组初始值设定项(以及其他)指定要使用正整数指示符设置数组的哪个元素($6.7.8.6、$6.7.8.17),例如:
const char *foo[] = {[2] = "foo", [1] = "bar", [0] = "baz"};
我以前用它来制作一个枚举到字符串 table,如下所示:
enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {
[THING_FOO] = "foo",
[THING_BAR] = "bar",
[THING_BAZ] = "baz"
}
但是,我现在的工作要求是我的代码符合 c89。
我研究了预处理器魔法(例如 here),但我需要字符串是任意的,而不是枚举符号的副本。
光做是不够的
enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {"foo", "bar", "baz"};
因为我以后需要添加枚举元素。使用 c99 方法,这将导致 table 中出现 NULL 指针,如果它们成为问题,则很容易调试。如果我忘记使用此方法更新字符串 table,我会得到更难调试的段错误。如果我无论如何都必须记住偏移量,它也会破坏具有符号的意义。
如果声明在函数中,我可以像这样达到预期的效果:
enum {THING_FOO = 0, THING_BAR, THING_BAZ, NUM_THINGS};
void foo(void)
{
static const char *table[NUM_THINGS];
table[THING_FOO] = "foo";
table[THING_BAR] = "bar";
table[THING_BAZ] = "baz";
/* ... */
}
但是,至少对于 gcc
,这 不会 得到优化。
有没有办法在c89中声明这样一个字符串table? (组装没问题。)
简单老套的怎么样
const char* table[] = { "foo", "bar", "baz" };
换句话说,只要按正确的顺序排列即可。
char *foo_string = table[FOO];
当然,这只适用于像上面这样的简单枚举,不适用于样式中的枚举
enum { FOO = 13; BAR = 15, BAZ = 312 };
但为此,您必须创建一个至少包含 313 个元素的数组,其中大部分元素无论如何都是 NULL,这将是一个非常浪费的构造。在这种情况下,当您使用 switch
结构时,编译器可以为您优化。
也看看S.O。 @Leandros 指出的问题:How to convert enum names to string in c。那里的答案使用宏来生成数组,确保条目的顺序正确。
或者,正如那个答案所说:
#define enum_str(s) #s
这完全摆脱了数组。
#define DEF_FOO_ENUM(E0, S0, E1, S1, E2, S2) \
enum foo { E0, E1, E2 }; \
const char *foo_str = { S0, S1, S2 };
DEF_FOO_ENUM(THING_FOO, "foo",
THING_BAR, "bar",
THING_BAZ, "baz");
符号和字符串是成对的。您不会轻易添加没有字符串的新符号,反之亦然。要添加一个元素,您必须向宏添加两个新参数——E3, S3
——等等。那里没有什么可以保持同步的,只是 enum
有所有的 E
-s 而数组有所有的 S
-s。这几乎是不可能搞砸的。
在尝试了几种不同的技术之后,这个最容易维护:
const char *table[] = {
#define FOO 0
"foo",
#define BAR (FOO + 1)
"bar",
#define BAZ (BAR + 1)
"baz"
}
这里聚集了一个条目的所有信息。要插入一个元素,你只需要修改它周围的东西。例如插入 qux
:
const char *table[] = {
#define FOO 0
"foo",
#define QUX (FOO + 1) /* new */
"qux", /* new */
#define BAR (QUX + 1) /* modified */
"bar",
#define BAZ (BAR + 1)
"baz"
}
它有点丑(有点可爱,你知道吗?)但效果很好。
您可以使用 X-Macros 将它们放在一起:
#define MYXMACRO(OP) \
OP(ENUM_FOO, "foo") \
OP(ENUM_BAR, " bar") \
OP(ENUM_BAZ, "baz")
/* use the first parameter to set up your enums*/
enum {
#define AS_ENUMS(x,y) x,
MYXMACRO(AS_ENUMS)
#undef AS_ENUMS /*not required, just playing nice*/
NUMTHINGS
};
/* use the 2nd parameter to set up your strings*/
const char *strings[] = {
#define AS_STRINGS(x,y) y,
MYXMACRO(AS_STRINGS)
#undef AS_STRINGS
};
#undef MYXMACRO
现在您的新数据可以作为一组枚举和字符串添加。如果您稍后决定根据枚举添加其他内容,则可以使用 'z' 参数轻松扩展到 OP() 甚至 ...
和 __VA_ARGS__
用于多个但数量不同的参数。