7

请参阅此测试程序:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
  if (argc < 2)
    goto end;

  char s[strlen(argv[1]) + 1];
  strcpy(s, argv[1]);
  printf("s=%s\n", s);

end:
  return 0;
}

它无法编译并出现错误“跳转到具有可变修改类型的标识符范围”(请参阅​​其他问题)。

但是,如果我将声明更改s为此(并包含alloca.h),它编译得很好:

char *s = alloca(strlen(argv[1]) + 1);

为什么 C 标准允许跳转到使用 创建的对象的范围alloca,而不是可变长度数组?我以为他们是等价的。

4

2 回答 2

2

这是因为编译器必须使用 VLA 运行时初始化范围的框架。换句话说,您告诉它跳转到地址:END,但您要求它跳过该范围的框架的初始化代码。

为 VLA 初始化空间的代码就在计算 VLA 长度的表达式之前。如果您跳过某些 goto 可以执行的代码,则所有程序都会出现段错误。

想象一下:

if (cond) goto end;
...
char a[expr];
end:
a[i] = 20;

在这种情况下,代码将简单地发生段错误,因为您跳转到 VLA a 的 mutator 但 a 未初始化。初始化 VLA 的代码必须插入到定义的位置。

现在关于alloca. 编译器也会这样做,但它无法检测到段错误。

所以这将出现段错误,编译器部分没有警告/错误。

逻辑与 VLA 相同。

int main(int argc, char *argv[])
{
    goto end;
    char *s = alloca(100);
end:
    s[1] = 2;
    return 0;
}

这就是他们在 ISO 9899 中插入声明的原因:

6.8.6.1 goto 语句——约束

1 goto 语句中的标识符应命名位于封闭函数某处的标签。goto 语句不应从具有可变修改类型的标识符范围之外跳转到该标识符范围内。

编译器在静态分析期间无法检测到此问题的正确答案,因为这实际上是halting problem.

于 2017-06-02T15:36:11.933 回答
2

如果程序在声明后跳转,除了释放 VLA 的问题外,还有一个sizeof.

想象一下你的程序被这样扩展:

end:
    printf("size of str: %zu\n", sizeof s);
    return 0;
}

对于alloca版本,sizeof s == sizeof(char*)可以在编译时计算,一切都很好。但是,对于 VLA 版本,长度s未知且sizeof s 无法计算。

于 2017-06-02T16:06:04.540 回答