如果我将可变长度数组写为本地,如下所示:
#include <stdio.h>
int main()
{
int i=1;
int a[i];
printf("%zu",sizeof(a));
}
它在GCC编译器中运行良好。
但是,如果我将可变长度数组写为全局数组,如下所示:
#include <stdio.h>
int i=1;
int a[i];
int main()
{
printf("%zu",sizeof(a));
}
然后,GCC编译器发出以下错误:
prog.c:4:5: error: variably modified 'a' at file scope
int a[i];
答案 0 :(得分:15)
来自标准6.7.6.2
如果标识符声明为具有静态或线程的对象 存储持续时间,它不应具有可变长度的数组类型。
答案 1 :(得分:4)
这是不允许的,因为除非它被限制到极端,否则它可能特别容易出错。考虑一下:
extern const int sz; // Another TU
int a[sz];
数组的大小取决于它与sz
之间的初始化顺序(在我们的示例中是在另一个跨国单位中)。它可能会产生0大小的数组(格式不正确)。正在建立的程序的正确性不应该取决于这些事情。
因此,如果我们仅将其大小限制为当前TU中的变量,我们最终得到:
const int sz = 10;
int a[sz];
但是在这种情况下为什么要使用VLA呢?只需使用常量表达式10指定大小。
这不是一个有用的功能。更不用说,正如@ M.M所指出的那样,它违背了设计目标,即容易将静态数据预先构建到二进制文件中。
答案 2 :(得分:4)
你已经have the answer,但只是详细说明为什么部分,让我加上我的两分钱。
首先,关于生命期 :(引用C11
,章节§6.2.4/ P2)
对象的生命周期是存储期间程序执行的一部分 保证为它保留。存在一个对象,具有恒定的地址,33)并保留 它在其整个生命周期中的最后存储价值.34)
然后,静态存储持续时间 :(引用P3)
在没有存储类说明符的情况下声明其标识符的对象
_Thread_local
,可以是外部或内部链接,也可以是存储类 说明符static
具有静态存储持续时间。它的一生就是整个执行 在程序启动之前,程序及其存储的值只初始化一次。
和链接 :(章节§6.2.3/ P5)
[...]如果 对象标识符的声明具有文件范围,没有存储类说明符, 它的联系是外部的。
因此,在这种情况下,a
将具有静态存储持续时间。
现在,根据定义,VLA维度是在运行时获取的,所以现在编译器可以知道并分配内存/存储并在开始初始化它(在程序之前)启动,根据静态存储持续时间的要求),因此这是冲突。
如C11
第6.7.6.2章所述,标准明确禁止。
[...]如果标识符被声明为具有静态或线程存储的对象 持续时间,它不应具有可变长度数组类型。
答案 3 :(得分:4)
因为标准是这样说的。但这并不是很有帮助; 为什么这个标准不允许的原因是所有文件范围变量必须在调用main()之前初始化。这反过来意味着它们必须只包含编译时常量。
VLA的目的是在运行时获取大小。我们不能有一个数组,在编译时和运行时确定大小。如果在文件范围需要可变大小的数组,请使用指针,然后在运行时将其指向已分配的数组,例如使用malloc()分配的数组。