可变大小的多维数组

Version >= C99

从 C99 开始,C 具有可变长度数组 VLA,该数组模拟具有仅在初始化时已知的边界的数组。虽然你必须小心不要分配太大的 VLA(它们可能会粉碎你的堆栈),使用指向 VLA 的指针并在 sizeof 表达式中使用它们很好。

double sumAll(size_t n, size_t m, double A[n][m]) {
    double ret = 0.0;
    for (size_t i = 0; i < n; ++i)
       for (size_t j = 0; j < m; ++j)
          ret += A[i][j]
    return ret;
}

int main(int argc, char *argv[argc+1]) {
   size_t n = argc*10;
   size_t m = argc*8;
   double (*matrix)[m] = malloc(sizeof(double[n][m]));
   // initialize matrix somehow
   double res = sumAll(n, m, matrix);
   printf("result is %g\n", res);
   free(matrix);
}

这里 matrix 是指向 double[m] 类型的元素的指针,sizeof 表达的 sizeof 确保它包含 n 这样的元素的空间。

所有这些空间都是连续分配的,因此可以通过一次调用 free 来解除分配。

语言中 VLA 的存在也会影响函数头中数组和指针的可能声明。现在,在数组参数的 [] 中允许使用通用整数表达式。对于这两个函数,[] 中的表达式使用参数列表中之前声明的参数。对于 sumAll,这些是用户代码对矩阵所期望的长度。对于 C 中的所有数组函数参数,最内层维度被重写为指针类型,因此这等同于声明

  double sumAll(size_t n, size_t m, double (*A)[m]);

也就是说,n 实际上并不是函数接口的一部分,但是这些信息对于文档很有用,它也可以被边界检查编译器用来警告越界访问。

Likwise,对于 main,表达式 argc+1 是 C 标准为 argv 参数规定的最小长度。

请注意,正式的 VLA 支持在 C11 中是可选的,但我们知道没有编译器实现 C11 并且没有它们。如果必须,你可以使用宏 __STDC_NO_VLA__ 进行测试。