C结构体使用标签进行初始化。它能够正常工作,但是为什么?

48

昨天我发现了一些结构体初始化代码,让我感到困惑。这里是一个例子:

typedef struct { int first; int second; } TEST_STRUCT;
void testFunc() {
    TEST_STRUCT test = {
        second: 2,
        first:  1
    };
    printf("test.first=%d test.second=%d\n", test.first, test.second);
}

令我惊讶的是,这里是输出结果:

-> testFunc
test.first=1 test.second=2

如您所见,结构体得到了正确的初始化。我不知道标记语句可以这样使用。我已经看过其他几种做结构体初始化的方法,但在任何在线C常见问题解答中都没有找到这种类型的结构体初始化的示例。有人知道为什么和如何工作吗?

5个回答

51

以下是gcc手册中有关结构体和数组指定初始化器语法的部分:

在结构体初始化器中,可以在元素值之前用“.fieldname =”指定要初始化的字段名称。例如,给定以下结构体:

 struct point { int x, y; };
以下初始化
 struct point p = { .y = yvalue, .x = xvalue }; 

等价于

 struct point p = { xvalue, yvalue }; 

另一种具有相同含义的语法,自GCC 2.5起已被弃用,是 'fieldname:',如下所示:

 struct point p = { y: yvalue, x: xvalue };

相关页面在这里可以找到。

你的编译器应该有类似的文档。


3
很好,该文档清晰地解释了语法:另一种具有相同含义的语法是 `fieldname:',自 GCC 2.5 以来已过时,如下所示: struct point p = { y: yvalue, x: xvalue }; - Andrew Cottrell
1
@AndrewCottrell,这个“fieldname:”语法对我来说看起来很自然(也更可取),你有什么想法觉得它为什么应该被视为过时了吗? - rick
4
@rick,“fieldname:”语法是gcc的扩展,从未成为任何ISO C标准的一部分。 - sigjuice

34

这些既不是标签也不是位域。

这是一种用于初始化结构成员的语法,可以追溯到 C99 之前的时代。它没有被标准化,但在例如 gcc 中可用。

typedef struct { int y; int x; } POINT;
POINT p = { x: 1, y: 17 };
在C99标准中首次引入了初始化特定结构成员的语法,但它看起来有些不同:
typedef struct { int y; int x; } POINT;
POINT p = { .x = 1, .y = 17 };

1
是的,我知道指定初始化程序的格式。不幸的是,这种格式与C++不兼容!(至少在我的测试中是这样。)不过还是谢谢你的回答。很高兴知道这不是标准化的。 - Andrew Cottrell
9
你从未在问题中提到过C ++。 - horseyguy

15

是的,如上所指出的那样,这些是指定初始化器,它们是标准的C语言,不过您应该改用句点而不是冒号。正如您所指出的,大多数书籍仍然停留在1984年左右的语法中,并未提及它们。更有趣的事实是:

- 当使用指定初始化器时,所有未指定的内容都会被初始化为零。这对于非常大的结构体很有帮助,例如:

typedef struct {
   double a, b, c, d, e;
   char label[100];
} too_many_type;

too_many_type tm = {.a = 1, .e = 2, .b=1.5};
assert(tm.a + tm.b + tm.c + tm.d + tm.e == 4.5);
assert(!strlen(label));

--此外,您还可以使用复合字面值形式在非初始化行上使用此形式,例如:

too_many_type tm2;
tm2 = (too_many_type) {.a = 3, .e=6};

这些功能非常优秀,而且被我所知道的所有C编译器支持,因为它们是标准。但很遗憾它们并不是那么出名。


1
使用冒号的替代语法看起来对我来说自然而且更可取,你能解释一下为什么我们应该改用句点而不是冒号吗? - rick

6

这并不是“标记语句”,而是一种为结构体中的命名字段赋初始值的方法。

Gcc会发出关于“使用已过时的指定初始化器‘:’”的警告,而在C99中,您应该改为编写:

    TEST_STRUCT test = {
        .second = 2,
        .first =  1
    };

3

这种语法在C标准中没有定义。第6.7.8 初始化节指出:

         designation:
                designator-list =
         designator-list:
                designator
                designator-list designator
         designator:
                [ constant-expression ]
                . identifier

如果你的编译器接受带有冒号的指定符且没有出现错误信息,那么这意味着你的编译器不符合标准(或者被配置为不符合标准)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接