Namespaces
Variants

Array initialization

From cppreference.net

当初始化 数组 类型的对象时,初始化式必须是 字符串字面量 (可选择用花括号括起)或是用花括号括起的数组成员初始化式列表:

= 字符串字面量 (1)
= { 表达式 , ... } (2) (C99前)
= { 指示符 (可选) 表达式 , ... } (2) (C99起)
= { } (3) (C23起)
1) 字符与宽字符数组的字符串字面值初始化器
2) 以逗号分隔的 常量 (C99前) 表达式列表,用作数组元素的初始化器 ,可选择使用形如 [ constant-expression ] = 的数组指示符 (C99起)
3) 空初始化器会对数组的每个元素进行空初始化

已知大小的数组和未知大小的数组可以被初始化 ,但变长数组除外 (since C99) (until C23) 变长数组只能进行空初始化 (since C23)

所有未被显式初始化的数组元素都将被 空初始化

目录

从字符串初始化

字符串字面量 (可选择用花括号括起)可用作匹配类型数组的初始化器:

  • 普通字符串字面量 和 UTF-8 字符串字面量 (C11 起) 可用于初始化任意字符类型的数组( char signed char unsigned char
  • L 前缀的宽字符串字面量可用于初始化与 wchar_t (忽略 cv 限定符)兼容的任意类型数组
  • 带 u 前缀的宽字符串字面量可用于初始化与 char16_t 兼容(忽略 cv 限定符)的任何类型数组
  • 带 U 前缀的宽字符串字面量可用于初始化与 char32_t 兼容(忽略 cv 限定符)的任何类型数组
(since C11)

字符串字面量的连续字节或宽字符串字面量的宽字符(包括终止空字节/字符)将初始化数组的元素:

char str[] = "abc"; // str 的类型为 char[4],存储内容为 'a', 'b', 'c', '\0'
wchar_t wstr[4] = L"猫"; // str 的类型为 wchar_t[4],存储内容为 L'猫', '\0', '\0', '\0'

如果数组的大小已知,它可能比字符串字面量的大小少一,这种情况下终止空字符会被忽略:

char str[3] = "abc"; // str 的类型为 char[3],包含 'a', 'b', 'c'

请注意,此类数组的内容是可修改的,这与直接使用 char * str = "abc" ; 访问字符串字面量时不同。

从花括号包围的列表进行初始化

当使用花括号包围的初始化器列表对数组进行初始化时,列表中的第一个初始化器会初始化索引为零的数组元素 (除非指定了指示符) (since C99) ,而每个后续的初始化器 若无指示符 (since C99) 则会初始化比前一个初始化器所初始化元素的索引大一的数组元素。

int x[] = {1,2,3}; // x 的类型为 int[3],包含值 1,2,3
int y[5] = {1,2,3}; // y 的类型为 int[5],包含值 1,2,3,0,0
int z[4] = {1}; // z 的类型为 int[4],包含值 1,0,0,0
int w[3] = {0}; // w 的类型为 int[3],包含全零值

在初始化已知大小的数组时,提供比元素数量更多的初始化式是错误的(除非使用字符串字面值初始化字符数组)。

指示符会使后续的初始化器初始化该指示符所描述的数组元素。随后初始化将按顺序向前继续,从该指示符描述的元素之后的下一个元素开始。

int n[5] = {[4]=5,[0]=1,2,3,4}; // 存储 1,2,3,4,5
int a[MAX] = { // 开始初始化 a[0] = 1, a[1] = 3, ...
    1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0
};
// 当 MAX=6 时,数组存储 1,8,6,4,2,0
// 当 MAX=13 时,数组存储 1,3,5,7,9,0,0,0,8,6,4,2,0("稀疏数组")
(C99 起)

当初始化未知大小的数组时,指定了初始化器的最大下标将决定所声明数组的大小。

嵌套数组

如果数组的元素是数组、结构体或联合体,则大括号括起的初始化器列表中对应的初始化器可以是适用于这些成员的任何有效初始化器,但可以按以下方式省略它们的大括号:

如果嵌套初始化器以左花括号开始,则整个嵌套初始化器直至其右花括号都会初始化对应的数组元素:

int y[4][3] = { // 由4个包含3个整数的数组组成的数组(4x3矩阵)
    { 1 },      // 第0行初始化为 {1, 0, 0}
    { 0, 1 },   // 第1行初始化为 {0, 1, 0}
    { [2]=1 },  // 第2行初始化为 {0, 0, 1}
};              // 第3行初始化为 {0, 0, 0}

如果嵌套初始化器不是以左花括号开始,则仅从列表中取足够数量的初始化器来对应子数组、结构体或联合体的元素或成员;任何剩余的初始化器将留作初始化下一个数组元素:

int y[4][3] = {    // 4个包含3个int的数组(4x3矩阵)
1, 3, 5, 2, 4, 6, 3, 5, 7 // 第0行初始化为 {1, 3, 5}
};                        // 第1行初始化为 {2, 4, 6}
                          // 第2行初始化为 {3, 5, 7}
                          // 第3行初始化为 {0, 0, 0}
struct { int a[3], b; } w[] = { { 1 }, 2 }; // 结构体数组
   // { 1 } 被视为数组第0个元素的完整花括号初始化器
   // 该元素被初始化为 { {1, 0, 0}, 0}
   // 2 被视为数组第1个元素的第一个初始化值
   // 该元素被初始化为 { {2, 0, 0}, 0}

数组指示符可以嵌套;嵌套数组的方括号常量表达式跟在外部数组的方括号常量表达式之后:

int y[4][3] = {[0][0]=1, [1][1]=1, [2][0]=1};  // 第0行初始化为 {1, 0, 0}
                                               // 第1行初始化为 {0, 1, 0}
                                               // 第2行初始化为 {1, 0, 0}
                                               // 第3行初始化为 {0, 0, 0}
(C99起)

注释

数组初始化器中子表达式的 求值顺序 在C语言中是不确定顺序的(但在C++中自C++11起不是):

int n = 1;
int a[2] = {n++, n++}; // 未指定但行为明确,
                       // n 被递增两次(顺序任意)
                       // a 初始化为 {1, 2} 或 {2, 1} 均有效
puts((char[4]){'0'+n} + n++); // 未定义行为:
                              // n 的递增与读取操作顺序不确定

在C语言中,初始化器的花括号列表不能为空。C++允许空列表:

(C23前)

空初始化器可用于初始化数组:

(C23起)
int a[3] = {0}; // 有效的C和C++方式将块作用域数组清零
int a[3] = {}; // 有效的C++方式将块作用域数组清零;自C23起在C语言中有效

与所有其他 初始化 一样,在初始化具有静态或线程局部 存储期 的数组时,初始化器列表中的每个表达式都必须是 常量表达式

static char* p[2] = {malloc(1), malloc(2)}; // 错误

示例

int main(void)
{
    // 以下四个数组声明是等价的
    short q1[4][3][2] = {
        { 1 },
        { 2, 3 },
        { 4, 5, 6 }
    };
    short q2[4][3][2] = {1, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 4, 5, 6};
    short q3[4][3][2] = {
        {
            { 1 },
        },
        {
            { 2, 3 },
        },
        {
            { 4, 5 },
            { 6 },
        }
    };
    short q4[4][3][2] = {1, [1]=2, 3, [2]=4, 5, 6};
    // 可以使用带指示符的数组将字符名称与枚举常量关联:
    enum { RED, GREEN, BLUE };
    const char *nm[] = {
        [RED] = "red",
        [GREEN] = "green",
        [BLUE] = "blue",
    };
}

参考文献

  • C17 标准 (ISO/IEC 9899:2018):
  • 6.7.9/12-39 初始化 (p: 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 初始化