9

C 标准禁止 goto 进入存在 VLA 的函数范围。

VLA 和对 alloca 函数的调用在低级别应该具有相同的结果。

(我可能是错的,因为我只是一个 C,不是低级程序员,但在我的想象中这似乎很机智)

那么下面的代码片段也会是未定义的行为吗?

int main()
{
    char *p;

    goto label1;

    {
        p = _alloca(1);
label1:
        p = NULL;
    }
}

当然我不能参考p,但是行为呢?

4

3 回答 3

5

Actually, the rule 6.8.6.1 states:

  A goto statement is not allowed to jump past any declarations of objects 
  with variably modified types.

In your code, there does not exist an object with variably modified types. alloca does not declare an object (that the compiler has to care of). Thus, there is nothing like a scope for alloca, and no reason for undefined behavior in the sense of rule 6.8.6.1.

EDIT

To elaborate the answer a bit: the "undefinedness" of the behavior in case of VLA is due to the promise of a declaration that an object is "known" within its scope (at language level). In general, a declaration sets a context for code execution. There is no need that it is executed at runtime. However, this is not true in case of VLA: here this promise is implemented partly at runtime, breaking C's static declaration approach. To avoid further conflicts that would lead to a dynamic typing system, rule 6.8.6.1 avoids such conflicts.

In contrast, at language level alloca is simply a function; its call does not constitute any scope. It makes only a promise about its run-time behavior in case it is called. If it isn't called, we do not "expect" anything from a function. Thus, its pure existence does not raise any conflict: both cases (bypassing or non-bypassing) have a well defined semantic.

于 2014-05-23T08:16:16.120 回答
1

VLA 和对 alloca 函数的调用在低级别应该具有相同的结果。

仍有一些差异。VLA 对象在声明它的作用域结束alloca时被丢弃,并且在函数返回时被分配的内存对象被丢弃。

这有所不同,因为 c99, 6.8.6.1p1 中的要求(“goto 语句不得从具有可变修改类型的标识符范围之外跳转到该标识符范围内”)与运行时分配有关/释放具有可变修改类型的对象。这里的alloca语句在执行后不goto执行,所以我认为goto调用不会调用未定义的行为。

于 2014-05-23T08:29:50.507 回答
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),这将导致alloca()行为不端。使用该goto版本的代码实际上可能比带有 的版本更安全if,因为编译器更难确定推迟清理foo可能是有利的。

于 2018-02-19T16:01:21.360 回答