3

我写了以下代码:

int tester(int n)
{
    int arr[n];
    // ...
}

此代码使用 g++ 编译,没有警告。

我的问题是——如何?参数 n 仅在运行时已知,在数组中是静态分配的。gcc如何编译这个?

4

2 回答 2

7

这是 GCC 为 C++ 提供的扩展,尽管自 C99 以来 C 正确支持可变长度数组(“VLA”)。

实施并不难。在典型的调用堆栈实现中,该函数只需要保存堆栈帧的基址,然后将堆栈指针前进动态指定的数量。VLA 总是带有一个警告,如果数字太大,你会得到未定义的行为(在 Stack Overflow 中表现出来),这使得它们的正确使用比std::vector.

曾经有人努力为 C++ 添加一个类似的特性,但这在类型系统方面却出人意料地困难(例如, 的类型是arr什么?它是如何在函数模板中推导出来的?)。这些问题在 C 中不太明显,它具有更简单的类型系统和对象模型(但话虽如此,您仍然可以争辩说 C 因拥有 VLA 而变得更糟,标准的很大一部分都花在了它们上,而且语言会没有它们会简单得多,而且不一定会更穷)。

于 2014-03-13T18:02:24.723 回答
3

GNU C 库提供了一个在堆栈上分配内存的函数 - alloca(3)。它只是递减堆栈指针,从而在其上创建一些暂存空间。GCC 用于alloca(3)实现 C99 可变长度数组 - 它首先递减函数序言中的堆栈指针以为所有自动变量创建空间,其大小在编译时已知,然后用于alloca(3)进一步递减它并为arr大小为在运行时确定。优化器实际上可能会融合这两个减量。

int tester(int n)
{
   int arr[n];
   return 0;
}

编译成

;; Function tester (tester)

tester (int n)
{
  int arr[0:D.1602D.1602] [value-expr: *arr.1];
  int[0:D.1602D.1602] * arr.1;
  long unsigned int D.1610D.1610;
  int n.0;
  ...

<bb 2>:
  n.0 = n;
  ...
  D.1609D.1609 = (long unsigned int) n.0;
  D.1610D.1610 = D.1609D.1609 * 4;
  D.1612D.1612 = __builtin_alloca (D.1610D.1610); <----- arr is allocated here
  arr.1 = (int[0:D.1602D.1602] *) D.1612D.1612;
  ...

这等效于以下 C 代码:

int tester(int n)
{
   int *arr = __builtin_alloca(n * sizeof(int));
   return 0;
}

__builtin_alloca()是 GCC 的内部实现alloca(3)

于 2014-03-13T18:03:19.507 回答