C标准禁止进入存在VLA的函数作用域。
VLA和调用alloc函数在底层应该有相同的结果。
(我可能是错的,因为我只是一个C,而不是一个低级程序员,但在我的想象中,这似乎是机智的)
那么下面的代码段也是未定义的行为吗?
int main()
{
char *p;
goto label1;
{
p = _alloca(1);
label1:
p = NULL;
}
}
当然我不能引用p
,但是它的行为呢?
实际上,规则6.8.6.1规定:
A goto statement is not allowed to jump past any declarations of objects
with variably modified types.
在您的代码中,不存在具有可变修改类型的对象。alloca
不而声明对象(编译器必须关心的对象)。因此,alloca
没有作用域,也没有理由出现规则6.8.6.1意义上的未定义行为。
编辑
详细说明一下答案:VLA中行为的"不确定性"是由于承诺声明对象在其作用域内(在语言级别)是"已知的"。通常,声明为代码执行设置上下文。没有必要在运行时执行它。然而,在VLA的情况下就不是这样了:在这里,这个承诺部分是在运行时实现的,打破了C的静态声明方法。为了避免可能导致动态类型系统的进一步冲突,规则6.8.6.1避免了此类冲突。
相比之下,在语言层面上,alloca
只是一个函数;它的调用不构成任何作用域。它只在被调用时对其运行时行为做出承诺。如果它没有被调用,我们就不"期望"从函数中得到任何东西。因此,它的纯粹存在不会引起任何冲突:两种情况(绕过或非绕过)都有一个定义良好的语义。
VLA和调用alloc函数在底层应该有相同的结果。
仍然有一些差异。VLA对象在声明它的作用域结束时被丢弃,alloca
分配的内存对象在函数返回时被丢弃。
这是有区别的,因为c99, 6.8.6.1p1中的要求("goto语句不能从具有可变修改类型的标识符的作用域外跳转到该标识符的作用域内")与运行时分配/释放具有可变修改类型的对象有关。这里alloca
语句没有在执行goto
之后执行,所以我认为goto
调用不会调用未定义的行为。
C标准对alloca()
的行为没有任何规定。一些编译器以非常可预测的方式使用堆栈,并使用大量冗余的帧指针访问自动变量。在这样的编译器上,可以通过简单地从堆栈指针中减去一个值来保留堆栈上的空间,而编译器不必知道或关心所讨论的保留。但是,如果编译器以应用程序不期望的方式使用堆栈,那么这样的代码将会严重出错。
我不认为像这样的东西:
int *p = 0;
if (!foo(1,2,3))
goto skip_alloca;
p=alloca(46);
skip_alloca:
bar(4,5,6);
...
往往比
更危险 int *p = 0;
if (foo(1,2,3))
p=alloca(46);
bar(4,5,6);
...
如果在执行alloca()
时函数调用的堆栈上没有剩余,则任何操作都可能是安全的。如果在分配时堆栈上有残留(例如,因为编译器选择延迟foo
的参数清理,直到调用bar
之后),这将使alloca()
行为不佳。使用goto
版本的代码实际上可能比使用if
版本的代码更安全,因为对于编译器来说,推迟从foo
进行清理可能是有利的,这将更难识别。