C
C 语法

Struct and union initialization

Struct and union initialization

在初始化struct或union类型的对象时,初始值设定项必须是成员的非空的,括号括起来的以逗号分隔的初始值设定项列表:

= {表达式,...}(直到C99)
= {指定者(可选)表达式,...}(自C99以来)

其中指示符是.表单[索引中的表单成员和数组指示符的各个成员标识符的序列(空格分隔或相邻)]

所有未明确初始化的成员都会以与具有静态存储持续时间的对象相同的方式隐式初始化。

说明

初始化联合时,初始化程序列表必须只有一个成员,它将初始化联合的第一个成员,除非使用了指定的初始化程序(自C99以来)。

union { int x; char c[4]; } u = {1}, // makes u.x active with value 1 u2 = { .c={'\1'} }; // makes u2.c active with value {'\1','\0','\0','\0'}

当初始化一个结构时,列表中的第一个初始化器初始化第一个声明的成员(除非指定了指定符)(自C99开始),并且没有指定符的所有后续初始化器(自C99开始)初始化在先前由表达。

struct point {double x,y,z;} p = {1.2, 1.3}; // p.x=1.2, p.y=1.3, p.z=0.0 div_t answer = {.quot = 2, .rem = -1 }; // order of elements in div_t may vary

指示符使下列初始化程序初始化由指定符描述的结构成员。然后按照声明的顺序继续初始化,从在指定符描述的声明之后声明的下一个元素开始。struct {int sec,min,hour,day,mon,year;} z = {.day = 31,12,2014,.sec = 30,15,17}; //将z初始化为{30,15,17,31,12,2014}(自C99以来)

提供比成员更多的初始化器是错误的。

嵌套初始化

如果结构体或联合体的成员是数组,结构体或联合体,则大括号括起来的初始化程序中相应的初始化程序是对这些成员有效的任何初始化程序,除了它们的大括号可以省略如下:

如果嵌套的初始化程序以一个左大括号开始,则整个嵌套初始化程序直到其大括号初始化相应的成员对象。每个左开口大括号建立一个新的当前对象当前对象的成员以自然顺序进行初始化,除非使用了指示符(自C99起):数组元素以下标顺序,声明顺序的struct成员,仅为任何联合的第一个声明成员。当前对象内的子对象没有被右括号显式初始化,它们被隐式初始化。

struct example { struct addr_t { uint32_t port; } addr; union { uint8_t a8[4]; uint16_t a16[2]; } in_u; }; struct example ex = { // start of initializer list for struct example { // start of initializer list for ex.addr 80 // initialized struct's only member }, // end of initializer list for ex.addr { // start of initializer-list for ex.in_u {127,0,0,1} // initializes first element of the union } };

如果嵌套的初始化程序不是以大括号开始,则只有足够的初始化程序才会考虑成员数组的元素或成员struct或union; 任何剩余的初始化器都将被初始化为下一个结构成员:

struct example ex = {80, 127, 0, 0, 1}; // 80 initializes ex.addr.port // 127 initializes ex.in_u.a8[0] // 0 initializes ex.in_u.a8[1] // 0 initializes ex.in_u.a8[2] // 1 initializes ex.in_u.a8[3]

When designators are nested, the designators for the members follow the designators for the enclosing structs/unions/arrays. Within any nested bracketed initializer list, the outermost designator refers to the current object and selects the subobject to be initialized within the current object only. struct example ex2 = { // current object is ex2, designators are for members of example .in_u.a80=127, 0, 0, 1, .addr=80}; struct example ex3 = {80, .in_u={ // changes current object to the union ex.in_u 127, .a82=1 // this designator refers to the member of in_u } }; If any subobject is explicitly initialized twice (which may happen when designators are used), the initializer that appears later in the list is the one used (the earlier initializer may not be evaluated): struct {int n;} s = {printf("a\n"), // this may be printed or skipped .n=printf("b\n")}; // always printed Although any non-initialized subobjects are initialized implicitly, implicit initialization of a subobject never overrides explicit initialization of the same subobject if it appeared earlier in the initializer list: #include typedef struct { int k; int l; int a2; } T; typedef struct { int i; T t; } S; T x = {.l = 43, .k = 42, .a1 = 19, .a0 = 18 }; // x initialized to {42, 43, {18, 19} } int main(void) { S l = { 1, // initializes l.i to 1 .t = x, // initializes l.t to {42, 43, {18, 19} } .t.l = 41, // changes l.t to {42, 41, {18, 19} } .t.a1 = 17 // changes l.t to {42, 41, {18, 17} } }; printf("l.t.k is %d\n", l.t.k // .t = x sets l.t.k to 42 explicitly // .t.l = 42 would zero out l.t.k implicitly } Output: l.t.k is 42 However, when an initializer begins with a left open brace, its current object is fully re-initialized and any prior explicit initializers for any of its subobjects are ignored: struct fred { char s4; int n; }; struct fred x = { { { "abc" }, 1 }, // inits x0 to { {'a','b','c','\0'}, 1 } 0.s0 = 'q' // changes x0 to { {'q','b','c','\0'}, 1 } }; struct fred y = { { { "abc" }, 1 }, // inits y0 to { {'a','b','c','\0'}, 1 } 0 = { // current object is now the entire y0 object .s0 = 'q' } // replaces y0 with { {'q','\0','\0','\0'}, 0 } };(自C99以来)

注释

任何初始化器中子表达式的评估顺序都是非确定的:

int n = 1; struct {int x,y;} p = {n++, n++}; // unspecified, but well-defined behavior: // n is incremented twice in arbitrary order // p equal {1,2} and {2,1} are both valid

在C中,初始化器的支撑列表不能为空(请注意,C ++允许空列表,并且还要注意C中的结构不能为空):

struct {int n;} s = {0}; // OK struct {int n;} s = {}; // Error: initializer-list cannot be empty struct {} s = {}; // Error: struct cannot be empty, initializer-list cannot be empty

与所有其他初始化一样,初始化器列表中的每个表达式在初始化静态或线程本地存储持续时间数组时都必须是常量表达式:

static struct {char* p} s = {malloc(1)}; // error

初始化程序列表可能会有一个尾随逗号,这会被忽略。

struct {double x,y;} p = {1.0, 2.0, // trailing comma OK };

#include <stdio.h> #include <time.h> int main(void) { char buff[70]; // designated initalizers simplify the use of structs whose // order of members is unspecified struct tm my_time = { .tm_year=112, .tm_mon=9, .tm_mday=9, .tm_hour=8, .tm_min=10, .tm_sec=20 }; strftime(buff, sizeof buff, "%A %c", &my_time puts(buff }

可能的输出:

Sunday Sun Oct 9 08:10:20 2012

参考

  • C11标准(ISO / IEC 9899:2011):