为什么我可以转到alloca:d变量的范围,但不是可变长度数组?

时间:2017-06-02 15:29:02

标签: c language-lawyer variable-length-array alloca

参见此测试程序:

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); 创建的对象的范围,而不是可变长度数组?我认为它们是等价的。

2 个答案:

答案 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 无法计算。