C99型VLA有哪些技术缺点

What technical disadvantages do C99-style VLAs have?

本文关键字:技术 缺点 VLA C99      更新时间:2023-10-16

我听很多人说C99中引入的可变长度数组很糟糕。IRC上的一些人一分钟前说:"我认为C++不会得到VLA,strousoup对他们发表了一些非常负面的评论"。

这些人讨厌VLA的原因是什么?

VLA 在运行时在堆栈上分配数组,这使得确定编译时使用的堆栈大小变得更加困难,甚至无法确定。由于堆栈的可用内存量相当少(与堆相比(,许多人担心 VLA 有很大的堆栈溢出可能性。

即将推出的MISRA-C编码标准版本很可能也将禁止VLA。

尽管可变长度数组有其问题,但应该记住它们是如何形成的: 作为alloca()的替代品,可以说问题更大

虽然在PDP-11上实现是微不足道的,但在其他架构上并非如此,Ritchie和Thompson将其从实现中删除。

然而,可变大小的自动分配显然足够有用,尽管它存在问题,alloca()还是复活了(特别是,它不能在可以调用任意函数的任何地方使用,并且在许多架构上它必须是内置的编译器(。C 工作组同意提供这样的功能,但认为可变长度数组是更好的解决方案。

如果你看一下用C99添加的功能(复数,类型泛型数学,restrict,...(,你应该注意到其中很多都是为了使C成为更好的数字计算语言。可变长度数组在那里也很有用,我相信 Fortran 当时已经有了它们。此外,它们的引入还导致了可变修改的派生类型(例如指向可变大小数组的指针(,这在处理矩阵时特别有用。

正如其他人指出的那样,VLA 使堆栈帧溢出变得非常容易。 我不是编译器编写者,但我的理解是VLA也可以成为支持的buger(它们现在在C2011中是可选的(。 它们的使用仅限于块或功能范围;不能在文件范围内使用 VLA,并且它们不能具有外部链接。

不过,我不希望看到VLA语法消失;在动态分配多维数组时,它非常方便,其中内部维度直到运行时才知道,例如:

size_t r, c;
// get values for r and c
int (*arr)[c] = malloc(r * sizeof *arr);
if (arr)
{
   ...
   arr[i][j] = ...;
   ...
   free(arr);
}

一个连续的分配(和一个相应的free(,我可以将其下标为 2D 数组。 替代方案通常意味着零碎分配:

size_t r, c;
...
int **arr = malloc(sizeof *arr * r);
if (arr)
{
  for (i = 0; i < c; i++)
    arr[i] = malloc(sizeof *arr[i] * c);
  ...
  arr[i][j] = ...;
  ...
  for (i = 0; i < c; i++)
    free(arr[i]);
  free(arr);
}

或使用一维偏移:

int *arr = malloc(sizeof *arr * r * c);
if (arr)
{
  ...
  arr[i * r + j] = ...;
  ...
  free(arr);
}

VLA 使溢出堆栈变得更加容易。在使用 VLA 的大多数位置,长度将基于其中一个函数参数。如果参数是您意想不到的,您最终可能会在堆栈上分配一个非常大的数组。除非您可以确定没有参数组合会导致堆栈溢出,否则您应该使用动态分配。

使用它们可能有意义的一个地方是在嵌入式平台上,因为在进行嵌入式编程时,您可能会足够密切地跟踪内存使用情况以确保不会发生堆栈溢出的少数情况之一。