1

C99 允许数组初始值设定项(除其他外)指定使用正整数指示符设置数组的哪个元素($6.7.8.6、$6.7.8.17),例如:

const char *foo[] = {[2] = "foo", [1] = "bar", [0] = "baz"};

我以前用它来制作一个枚举到字符串的表,如下所示:

enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {
    [THING_FOO] = "foo",
    [THING_BAR] = "bar",
    [THING_BAZ] = "baz"
}

但是,我现在的工作要求是我的代码符合 c89。

我已经研究过预处理器魔法(例如在这里),但我需要字符串是任意的,而不是枚举符号的副本。

仅仅做是不够的

enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {"foo", "bar", "baz"};

因为我将来需要添加枚举元素。使用 c99 方法,这将导致表中出现 NULL 指针,如果它们出现问题,可以很容易地进行调试。如果我忘记使用这种方法更新字符串表,我会得到更难调试的段错误。如果无论如何我都必须记住偏移量,它也会破坏拥有符号的意义。

如果声明在一个函数中,我可以达到这样的预期效果:

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 中声明这样的字符串表?(组装没问题。)

4

4 回答 4

2
 #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。这几乎是不可能搞砸的。

于 2016-07-23T23:27:21.353 回答
1

简单的,老式的呢?

const char* table[] = { "foo", "bar", "baz" };

换句话说,只需将它们按正确的顺序排列即可。

char *foo_string = table[FOO];

当然,这只适用于像上面这样的简单枚举,不适用于样式中的枚举

enum { FOO = 13; BAR = 15, BAZ = 312 };

但为此,您必须创建一个至少包含 313 个元素的数组,其中大多数元素无论如何都是 NULL,这将是一个非常浪费的构造。在这种情况下,当您使用switch构造时,编译器可以为您优化。


另请查看@Leandros 指出的 SO 问题:How to convert enum names to string in c。那里的答案使用宏来生成数组,以确保条目的顺序正确。

或者,正如那个答案所说:

#define enum_str(s) #s

这完全摆脱了数组。

于 2016-07-23T21:31:02.837 回答
1

您可以使用 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__多个但不同数量的参数。

于 2016-08-24T05:07:49.320 回答
0

在尝试了几种不同的技术之后,这个最容易维护:

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"
}

它有点难看(有点可爱,你知道吗?)但效果很好。

于 2016-08-24T03:30:57.723 回答