请参阅此测试程序:
#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=%sn", s);
end:
return 0;
}
它无法与"跳入具有可变修改类型的标识符范围"的错误编译(请参阅其他问题(。
但是,如果我将s
的声明更改为此(并包含alloca.h
(,则可以编译罚款:
char *s = alloca(strlen(argv[1]) + 1);
为什么C标准允许跳入使用alloca
创建的对象的范围,而不是可变长度数组?我以为它们是等效的。
这是因为编译器必须使用VLAS运行范围框架。换句话说,您告诉它跳到地址:END
,但您要求它跳过该范围框架的初始化代码。
初始化VLA空间的代码就在计算VLA长度的表达式之前。如果跳过该代码,某些Goto可以做的,所有程序都将符合。
想象类似:
if (cond) goto end;
...
char a[expr];
end:
a[i] = 20;
在这种情况下,当您跳到vla a的突变器时,代码将简单地示出,但没有初始化。必须插入定义的初始化VLA的代码。
现在关于alloca
。编译器将执行相同的操作,但无法检测到segfault。
所以这将使编译器的部分没有警告/错误。
逻辑与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语句中的标识符应为标签命名 封闭功能中的某个地方。一个声明不会跳 从具有变量修改的标识符范围外 输入该标识符的范围内。
编译器在静态分析期间无法检测到此问题的正确答案,因为这实际上是halting problem
。
除了该程序在声明后跳跃的问题外, sizeof
也有一个问题。
想象一下您的程序已扩展为:
end:
printf("size of str: %zun", sizeof s);
return 0;
}
对于alloca
版本,sizeof s == sizeof(char*)
,可以在编译时计算,一切都很好。但是,对于VLA版本,s
的长度未知,并且sizeof s
无法计算。