我写了以下代码:
int tester(int n)
{
int arr[n];
// ...
}
使用g ++编译此代码,没有警告。
我的问题是 - 如何?参数n在运行时是已知的,在数组中是静态分配的。 gcc是如何编译的?
答案 0 :(得分:7)
这是GCC为C ++提供的扩展,尽管自C99以来C可以正确支持可变长度数组(" VLA")。
实施并不是非常困难;在典型的调用堆栈实现中,该函数只需要保存堆栈帧的基础,然后按动态指定的量推进堆栈指针。如果数量太大,你会得到未定义的行为(在Stack Overflow中显示),这使得它们比使用std::vector
更加苛刻。
在某种程度上,我们已经努力为C ++添加一个类似的功能,但就类型系统而言,这种结果出乎意料地难以理解(例如arr
的类型是什么?它是如何推断出来的?功能模板?)。这个问题在C语言中不那么明显,它有一个更简单的类型系统和对象模型(但是说,你仍然认为C对于拥有VLA更糟糕,标准的相当一部分花费在它们上,语言会如果没有它们,它们会变得更加简单,并且不一定会更糟糕。)
答案 1 :(得分: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)
的内部实施。