C标准禁止转入存在VLA的功能范围。
VLA和对alloca函数的调用应该在低级别上具有相同的结果。
(我可能错了,因为我只是一个C,不是一个低级程序员,但在我的想象中似乎很机智)
以下代码段也是未定义的行为吗?
int main()
{
char *p;
goto label1;
{
p = _alloca(1);
label1:
p = NULL;
}
}
当然我不能参考p
,但是关于这种行为是什么?
答案 0 :(得分:4)
实际上,规则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的意义上存在未定义的行为。
修改强>
详细说明答案:" undefinedness"在VLA的情况下的行为是由于承诺声明一个对象是已知的"在其范围内(语言层面)。通常,声明为代码执行设置上下文。它不需要在运行时执行。然而,在VLA的情况下情况并非如此:这里的承诺部分在运行时实现,破坏了C的静态声明方法。为了避免导致动态类型系统的进一步冲突,规则6.8.6.1避免了这种冲突。
相比之下,在语言层面alloca
只是一个功能;它的召唤不构成任何范围。它只是对它的运行时行为的承诺,以防它被调用。如果它没有被召唤,我们就不会期望"任何功能。因此,它的纯粹存在不会引起任何冲突:两种情况(绕过或不绕过)都有明确定义的语义。
答案 1 :(得分:1)
VLA和对alloca函数的调用应该在低级别上具有相同的结果。
仍然存在一些差异。当声明它的作用域结束并且函数返回时,alloca
分配的内存对象被丢弃时,将丢弃VLA对象。
这有所不同,因为c99,6.8.6.1p1("一个goto语句)中的要求不得从具有可变修改类型的标识符范围之外跳转到该标识符范围内#34; )关注具有可变修改类型的对象的运行时分配/释放。这里alloca
语句在执行goto
后不会执行,因此我认为goto
调用不会调用未定义的行为。
答案 2 :(得分:1)
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()
时函数调用在堆栈中没有残留,则任一操作都可能是安全的。如果在alloca时堆栈上有残留(例如,因为编译器选择推迟清除foo
的参数,直到调用bar
之后)才会生成{{ 1}}行为不端。使用代码的alloca()
版本实际上可能比具有goto
的版本更安全,因为编译器更难以识别推迟从if
清除可能是有利的。< / p>