参见此测试程序:
Option Explicit
Public Sub Test()
Dim lngCounter As Long
Dim lngcounter2 As Long
On Error Resume Next
Cells.Clear
For lngCounter = 1 To 5
Debug.Print lngCounter / IIf(lngCounter Mod 2 = 0, 1, 0)
If Err.Number <> 0 Then
Cells(lngCounter, 1) = Err.Description
End If
Err.Clear
Next lngCounter
On Error GoTo 0
End Sub
无法编译错误&#34;跳转到具有可变修改类型的标识符范围&#34; (见other question)。
但如果我将#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
为什么C标准允许跳入使用char *s = alloca(strlen(argv[1]) + 1);
创建的对象的范围,而不是可变长度数组?我认为它们是等价的。
答案 0 :(得分:2)
这是因为编译器必须使用VLA运行时初始化作用域的帧。换句话说,你告诉它跳转到地址:END
,但你要求它跳过该范围框架的初始化代码。
初始化VLA空间的代码就在计算VLA长度的表达式之前。如果你跳过那些goto可以做的代码,那么所有的程序都会出现段错误。
想象一下:
if (cond) goto end;
...
char a[expr];
end:
a[i] = 20;
在这种情况下,代码只是段错误,因为你跳转到VLA的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
。
答案 1 :(得分:2)
除了在声明声明之后程序跳过时解除分配VLA的问题,sizeof
也存在问题。
想象一下,你的程序扩展了这个:
end:
printf("size of str: %zu\n", sizeof s);
return 0;
}
对于alloca
版本sizeof s == sizeof(char*)
,可以在编译时计算,一切都很好。但是,对于VLA版本,s
的长度未知,sizeof s
无法计算。