我写了以下代码:
int tester(int n)
{
int arr[n];
// ...
}
此代码使用 g++ 编译,没有警告。
我的问题是——如何?参数 n 仅在运行时已知,在数组中是静态分配的。gcc如何编译这个?
我写了以下代码:
int tester(int n)
{
int arr[n];
// ...
}
此代码使用 g++ 编译,没有警告。
我的问题是——如何?参数 n 仅在运行时已知,在数组中是静态分配的。gcc如何编译这个?
这是 GCC 为 C++ 提供的扩展,尽管自 C99 以来 C 正确支持可变长度数组(“VLA”)。
实施并不难。在典型的调用堆栈实现中,该函数只需要保存堆栈帧的基址,然后将堆栈指针前进动态指定的数量。VLA 总是带有一个警告,如果数字太大,你会得到未定义的行为(在 Stack Overflow 中表现出来),这使得它们的正确使用比std::vector
.
曾经有人努力为 C++ 添加一个类似的特性,但这在类型系统方面却出人意料地困难(例如, 的类型是arr
什么?它是如何在函数模板中推导出来的?)。这些问题在 C 中不太明显,它具有更简单的类型系统和对象模型(但话虽如此,您仍然可以争辩说 C 因拥有 VLA 而变得更糟,标准的很大一部分都花在了它们上,而且语言会没有它们会简单得多,而且不一定会更穷)。
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)
。