这里有两种使用初始化器列表来初始化数组的可能方法:
int arr[2] = {1, 2};
并将值一一赋值给数组:
int arr[2];
arr[0] = 1;
arr[1] = 2;
在 Godbolt 上使用 gcc 13.2(带有 -O0 优化标志)编译它们会产生完全相同的汇编代码:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-8], 1
mov DWORD PTR [rbp-4], 2
mov eax, 0
pop rbp
ret
但是,当我使用可变长度数组(VLA)尝试此操作时,这两种方法不仅不等效,而且第二种方法还会给出错误:
size_t n = 2;
int arr[n];
arr[0] = 1;
arr[1] = 2; // OK
size_t n = 2;
int arr[n] = {1, 2}; // EROR
这两种初始化方式的根本区别是什么?为什么对 VLA 使用初始化列表会失败?
arr[0] = 1;
不是初始化而是(运行时)赋值。它可能对生成的机器代码有影响,也可能没有影响,但语言方面的初始化和赋值是不同的概念(在 C++ 中更是如此)。
VLA 无法使用初始化列表进行初始化,因为 C 标准只是禁止这样做1)。但大多数时候,这样做没有多大意义:如果你知道有多少项,为什么要使用 VLA 而不是固定大小的数组?
明显的解决方法是
memcpy
:
size_t n;
int arr[n];
int some_init_list[] = {1,2,3,4,5};
memcpy(arr, some_init_list, sizeof arr);
或者如果你愿意的话:
memcpy(arr, (int[]){1,2,3,4,5}, sizeof arr);
部分初始化可以通过提前在 VLA 上按
memset
来完成,也可以通过在复制之前用零填充初始化列表中的剩余项目来完成。另外如评论中所述,即将推出的 C23 将支持空初始化列表int vla[n]={};
,以将 VLA 归零。
1) C17 6.7.9 约束: “要初始化的实体类型应为未知大小的数组或非变长数组类型的完整对象类型。”