在 C89 中,数组的长度在编译时是已知的。但是在 C99 中,对于可变长度数组,数组的长度在运行之前可能是未知的。
那么它是如何计算的呢?
为什么不能以同样的方式计算动态分配数组的长度?
在 C89 中,数组的长度在编译时是已知的。但是在 C99 中,对于可变长度数组,数组的长度在运行之前可能是未知的。
那么它是如何计算的呢?
为什么不能以同样的方式计算动态分配数组的长度?
来自 ISO/IEC 9899:TC3 第 6.7.5.2 节:数组声明符
具有可变修改类型的普通标识符(如 6.2.3 中定义)应具有块范围,并且没有链接或函数原型范围。如果标识符被声明为具有静态存储持续时间的对象,则它不应具有可变长度数组类型。
VLA 的大小很简单sizeof(vla_element_type) * vla_length
。由于 VLA 只能在块内定义its length must be either a local variable or a function parameter
,因此编译器可以在访问 vla 时访问该块。(由于 vla 的长度和 vla 本身属于同一个栈帧)。
Here is an example:
int main(int argc, char* argv[])
{
int m;
scanf("%d\n", &m);
int a[m];
printf("%d\n", sizeof(a));
return 0;
}
编译后clang -o test.ll -O2 -emit-llvm -S test.c
生成的IR如下图:
define i32 @main(i32 %argc, i8** nocapture %argv) nounwind {
entry:
// Allocate space on stack for m
%m = alloca i32, align 4
// call scanf
%call = call i32 (i8*, ...)* @__isoc99_scanf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32* %m) nounwind
// %0 now contains the value of m
%0 = load i32* %m, align 4, !tbaa !0
// %1 is m << 2, which is m * sizeof(int)
%1 = shl nuw i32 %0, 2
// call printf, output m * sizeof(int) to screen.
%call1 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %1) nounwind
// DONE.
ret i32 0
}
VLA 和malloc
通过指针变量保存的 ed 数组(除了位于内存的不同部分)之间的区别在于编译器在编译时知道第一个是数组。它可以与 VLA 一起在某个地方保存大小信息,所以基本上这是某种隐藏变量。根据您对该变量的使用情况,例如,如果您使用sizeof
它,或者您通过诸如A[i][j]
编译器之类的东西索引 2D VLA,则可以确定是否确实需要该隐藏变量,如果不需要,则优化它。