在C99中,如何在运行时计算变长数组的大小?

12
在C89中,数组的长度在编译时就已知。但是在C99中,由于可变长度数组的引入,数组的长度可能在运行时才确定。
那么它是如何计算的呢?
为什么不能以同样的方式计算动态分配数组的长度呢?
2个回答

6
VLA和malloc分配的数组的区别(除了存储在不同的内存部分之外),就是编译器能够在编译时知道前者是一个数组。它可以将大小信息与VLA一起存储在某个地方,基本上这是某种隐藏的变量。根据您对该变量进行的使用,例如如果您使用sizeof,或者通过A [i] [j]索引2D VLA,编译器可以决定是否真正需要该隐藏变量,如果不需要,则优化掉它。

易于理解!谢谢。很抱歉我不能为您点赞,因为我的声望只有11。 - minh.hieu

6

来自ISO/IEC 9899:TC3第6.7.5.2节:数组声明符

具有可变类型的普通标识符(如6.2.3中定义的)应该具有块作用域和无链接或函数原型作用域。如果将标识符声明为具有静态存储期的对象,则不得具有可变长度数组类型。

VLA的大小只是sizeof(vla_element_type) * vla_length。由于VLA只能在块内定义,它的长度必须是本地变量或函数参数,当访问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
}

可以这样定义一个函数:foo(int array[size], int size),以便我可以在该函数内使用sizeof(array)来计算array的大小吗? - minh.hieu
1
不,这是不可能的。数组参数将被编译器转换为指针。请也参考这篇文章:https://dev59.com/7m035IYBdhLWcg3wT-RC - Lei Mou
嗨,我是个GCC/CLANG的新手:IR指的是什么? - Cole Tobin
IR代表中间表示:http://en.wikipedia.org/wiki/Intermediate_language#Intermediate_representation - Lei Mou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接