Struct and union initialization
当初始化 结构体 或 联合体 类型的对象时,初始化器必须是由成员初始化器组成的 非空 (C23前) 花括号括起的逗号分隔列表:
=
{
表达式
,
...
}
|
(1) | (C99前) | |||||||
=
{
指示符
(可选)
表达式
,
...
}
|
(2) | (C99起) | |||||||
=
{
}
|
(3) | (C23起) | |||||||
其中
指示符
是由单个成员指示符(形式为
.
成员
)和
数组指示符
(形式为
[
索引
]
)组成的序列(以空格分隔或相邻)。
所有未显式初始化的成员都将被 空初始化 。
目录 |
说明
当初始化 联合体 时,初始化列表只能包含一个成员,该成员用于初始化联合体的第一个成员 除非使用指定初始化器 (C99起) 。
union { int x; char c[4]; } u = {1}, // 使 u.x 成为活动成员,其值为 1 u2 = { .c={'\1'} }; // 使 u2.c 成为活动成员,其值为 {'\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 }; // div_t 结构体中元素的顺序可能有所不同
|
指示符会使后续的初始化器初始化该指示符所描述的结构体成员。随后初始化按声明顺序继续进行,从该指示符描述成员之后声明的下一个元素开始。 struct {int sec,min,hour,day,mon,year;} z = {.day=31,12,2014,.sec=30,15,17}; // initializes z to {30,15,17,31,12,2014} |
(since C99) |
提供比成员数量更多的初始化器是错误的。
嵌套初始化
如果结构体或联合体的成员是数组、结构体或联合体,则在大括号括起的初始化器列表中对应的初始化器可以是适用于这些成员的任何初始化器,但可按以下方式省略其大括号:
如果嵌套初始化器以左花括号开始,整个嵌套初始化器直到其右花括号为止将初始化对应的成员对象。每个左花括号都会建立一个新的 当前对象 。当前对象的成员按其自然顺序进行初始化 ,除非使用了指示符 (C99起) :数组元素按下标顺序,结构体成员按声明顺序,联合体仅初始化首个声明成员。当前对象中未被右花括号显式初始化的子对象将被 空初始化 。
如果嵌套初始化器不是以左花括号开始,则仅从列表中取用足够数量的初始化器来对应成员数组、结构体或联合体的元素或成员;任何剩余的初始化器将留作初始化下一个结构体成员:
struct example ex = {80, 127, 0, 0, 1}; // 80 初始化 ex.addr.port // 127 初始化 ex.in_u.a8[0] // 0 初始化 ex.in_u.a8[1] // 0 初始化 ex.in_u.a8[2] // 1 初始化 ex.in_u.a8[3]
|
当指示符嵌套时,成员指示符跟随其外层结构体/联合体/数组的指示符。在任何嵌套的括号初始化列表中,最外层的指示符指向 当前对象 ,并仅选择 当前对象 中要初始化的子对象。 struct example ex2 = { // 当前对象是 ex2,指示符用于 example 的成员 .in_u.a8[0]=127, 0, 0, 1, .addr=80}; struct example ex3 = {80, .in_u={ // 将当前对象更改为联合体 ex.in_u 127, .a8[2]=1 // 此指示符引用 in_u 的成员 } }; 如果任何子对象被显式初始化两次(可能在使用指示符时发生),则采用初始化列表中靠后的初始化器(靠前的初始化器可能不会被求值): 尽管所有未初始化的子对象会被隐式初始化,但如果同一子对象在初始化列表中更早位置已被显式初始化,隐式初始化绝不会覆盖该显式初始化(选择 clang 观察正确输出):
运行此代码
#include <stdio.h> typedef struct { int k; int l; int a[2]; } T; typedef struct { int i; T t; } S; T x = {.l = 43, .k = 42, .a[1] = 19, .a[0] = 18 }; // x 初始化为 {42, 43, {18, 19} } int main(void) { S l = { 1, // 将 l.i 初始化为 1 .t = x, // 将 l.t 初始化为 {42, 43, {18, 19} } .t.l = 41, // 将 l.t 更改为 {42, 41, {18, 19} } .t.a[1] = 17 // 将 l.t 更改为 {42, 41, {18, 17} } }; printf("l.t.k is %d\n", l.t.k); // .t = x 显式将 l.t.k 设为 42 // .t.l = 41 会隐式清零 l.t.k } 输出: l.t.k is 42 然而,当初始化器以左花括号开头时,其 当前对象 会被完全重新初始化,之前对其任何子对象的显式初始化都将被忽略: struct fred { char s[4]; int n; }; struct fred x[ ] = { { { "abc" }, 1 }, // 将 x[0] 初始化为 { {'a','b','c','\0'}, 1 } [0].s[0] = 'q' // 将 x[0] 更改为 { {'q','b','c','\0'}, 1 } }; struct fred y[ ] = { { { "abc" }, 1 }, // 将 y[0] 初始化为 { {'a','b','c','\0'}, 1 } [0] = { // 当前对象现在是整个 y[0] 对象 .s[0] = 'q' } // 将 y[0] 替换为 { {'q','\0','\0','\0'}, 0 } }; |
(C99 起) |
注释
初始化列表可以包含一个被忽略的尾随逗号。
struct {double x,y;} p = {1.0, 2.0, // 末尾逗号允许 };
|
在C语言中,带括号的初始化列表不能为空(注意C++允许空列表,同时注意C语言中的 结构体 不能为空): |
(C23前) |
|
初始化列表在C语言中可如同C++一般留空: |
(C23起) |
struct {int n;} s = {0}; // 正确 struct {int n;} s = {}; // C23 前错误:初始化列表不能为空 // C23 起正确:s.n 被初始化为 0 struct {} s = {}; // 错误:结构体不能为空
|
初始化任意存储期聚合体时,初始化器列表中的每个表达式都必须是 常量表达式 。 |
(C99前) |
|
与所有其他 初始化 一样,当初始化静态 或线程局部 (C11起) 存储期 的聚合体时,初始化器列表中的每个表达式都必须是 常量表达式 : static struct {char* p} s = {malloc(1)}; // error 任何初始化器中子表达式的 求值顺序 是 indeterminately sequenced(但自C++11起在C++中不是): int n = 1; struct {int x,y;} p = {n++, n++}; // 未指定但行为良好定义: // n以任意顺序递增两次 // p等于{1,2}和{2,1}均有效 |
(C99起) |
示例
|
本节内容不完整
原因:需要更多实际示例,可能需要初始化某些套接字结构体 |
可能的输出:
Sunday Sun Oct 9 08:10:20 2012
参考文献
- C17 标准 (ISO/IEC 9899:2018):
-
- 6.7.9/12-39 初始化 (页码: 101-105)
- C11 标准 (ISO/IEC 9899:2011):
-
- 6.7.9/12-38 初始化 (p: 140-144)
- C99标准(ISO/IEC 9899:1999):
-
- 6.7.8/12-38 初始化(页码:126-130)
- C89/C90 标准 (ISO/IEC 9899:1990):
-
- 6.5.7 初始化
参见
|
C++ 文档
关于
Aggregate initialization
|