简体   繁体   English

c89中的代号

[英]Designators in c89

C99 allows array initializers (among others) to specify which element of the array is being set with a positive integer designator ($6.7.8.6, $6.7.8.17), for example like so: C99允许数组初始值设定项(除其他外)指定使用正整数指示符($ 6.7.8.6,$ 6.7.8.17)设置数组的哪个元素,例如:

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

I have previously used this to make an enum-to-string table like so: 我以前用它来制作一个枚举到字符串的表,如下所示:

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

However, I am now working under the requirement that my code is c89 compliant. 但是,我现在的工作是我的代码符合c89。

I have looked into preprocessor magic (as in here , for example) but I need the strings to be arbitrary, not copies of the enum symbols. 我研究了预处理器魔术(例如,在此处 ),但是我需要字符串是任意的,而不是枚举符号的副本。

It isn't sufficient to just do 仅仅做是不够的

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

because I will need to add enum elements in the future. 因为将来我需要添加枚举元素。 Using the c99 method, this would result in NULL pointers in the table which are acceptably easy to debug if they become problems. 使用c99方法,这将导致表中的NULL指针出现问题,如果出现问题,可以很容易地调试它们。 If I forgot to update the string table using this method, I'd get segfaults which are harder to debug. 如果我忘记使用此方法更新字符串表,则会遇到难以调试的段错误。 Also it defeats the point of having symbols if I have to remember offsets anyway. 如果我仍然要记住偏移量的话,那也就挫败了使用符号的问题。

If the declaration were in a function, I could achieve the desired effect like this: 如果声明在函数中,则可以实现如下所示的预期效果:

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

    /* ... */
}

However, at least with gcc , this does not get optimized. 但是,至少对于gcc ,并没有得到优化。

Is there any way of declaring such a string table in c89? 有什么方法可以在c89中声明这样的字符串表? (It's no problem in assembly.) (组装没有问题。)

 #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");

The symbols and strings are paired. 符号和字符串是成对的。 You're not easily going to add a new symbol without a string, or vice versa. 您不容易添加没有字符串的新符号,反之亦然。 To add an element, you have to two new arguments to the macro— E3, S3 —and so on. 要添加元素,您必须为宏添加两个新的参数-E3 E3, S3等。 There is nothing to keep in sync there, just that the enum has all the E -s and the array has all the S -s. 在那里没有什么要保持同步的,只是enum具有所有E -s,而数组具有所有S -s。 This is almost impossible to screw up. 这几乎是不可能的。

What about the simple, old fashioned 那简单,老式的呢

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

In other words, just put them in the correct order. 换句话说,只需按正确的顺序放置它们即可。

char *foo_string = table[FOO];

Of course, that only works for simple enums like the above, not for enums in the style 当然,这仅适用于上述简单枚举,不适用于该样式的枚举

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

But for that, you would have to create an array with at least 313 elements, most of which are NULL anyway, which would be a pretty wasteful construct. 但是为此,您必须创建一个至少包含313个元素的数组,无论如何大多数元素都为NULL,这将是非常浪费的构造。 In such cases, the compiler can optimize this for you, when you use a switch construct. 在这种情况下,当您使用switch构造时,编译器可以为您优化此过程。


Also take a look at the SO question @Leandros pointed to: How to convert enum names to string in c . 还可以看看SO问题@Leandros指出: 如何在c中将枚举名称转换为字符串 The answer there uses macros to generate the array, which ensures the entries are in the correct order. 那里的答案使用宏来生成数组,以确保输入顺序正确。

Or, as that answer says: 或者,如该回答所示:

#define enum_str(s) #s

Which gets rid of the array altogether. 这完全摆脱了数组。

You can keep them together by using X-Macros: 您可以使用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

Now your new data can be added as a set of enum and string. 现在,您可以将新数据添加为一组枚举和字符串。 If you later decided to add something else based on the enums it's easily extended with a 'z' parameter to OP() or even ... and __VA_ARGS__ for multiple but varying number of parameters. 如果您后来决定基于枚举添加其他内容,则可以在OP()或什至...__VA_ARGS__为多个但数量可变的参数添加一个“ z”参数来轻松扩展它。

After trying a few different techniques, this one is easiest to maintain: 在尝试了几种不同的技术之后,最容易维护这一技术:

const char *table[] = {
#define FOO 0
    "foo",
#define BAR (FOO + 1)
    "bar",
#define BAZ (BAR + 1)
    "baz"
}

Here all the information about an entry is clustered. 在这里,有关条目的所有信息均被聚集。 To insert an element you only have to modify stuff right around it. 要插入一个元素,您只需要修改周围的内容即可。 For example to insert qux : 例如插入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"
}

It's a bit ugly (kind of endearing, you know?) but it works good. 这有点丑陋(有点可爱,你知道吗?),但是效果很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM