跳转到 VLA 阵列上时出现分段错误

Segmentation fault when jumping to goto over VLA array

以下示例演示了该问题:

#include <cstdio>

int main()
{
        unsigned int remaining=1;

        goto loop;

        while(remaining) {
                unsigned char tmp[remaining];
                printf("&tmp: %p\n",tmp);
loop:
                remaining = 512;//or something else;
        }
}

最初,"remaining"变量的初始化有点长,我用goto一行初始化。但是,现在这个例子在 printf 行给出了分段错误。

数组似乎没有正确初始化。

即使 gdb 也无法打印 tmp 数组的地址:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004005b8 in main () at test.cpp:11
11          printf("&tmp: %p\n",tmp);
(gdb) p tmp
 = 0xfffffffffffffe00 <error: Cannot access memory at address 0xfffffffffffffe00>

我的 gcc 版本:

gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2

编译:

g++ -o testc test.cpp

如果我删除 goto,或用固定数组替换可变数组,分段错误就会消失。 到底发生了什么?

这是 gcc 错误吗?如果不允许 goto 和可变数组的组合,应该有警告?

可变长度数组(VLA)是 gcc 支持的 C99 特性 an extension in C++ 并且在 C99 中,跨 VLA 声明的跳转是未定义的行为,来自草案C99标准部分6.8.6.1 goto语句:

A goto statement shall not jump from outside the scope of an identifier having a variably modified type to inside the scope of that identifier.

clanggcc 4.9 实际上使这个错误并说:

error: goto into protected scope
    goto loop;
    ^

note: jump bypasses initialization of variable length array
            unsigned char tmp[remaining];
                          ^

参见gcc bug report: Jumps into VLA or VM scope not rejected for C++

在 C++ 中跳过自动变量声明的规则在 6.7 [stmt.dcl] 节中介绍:

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps87 from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5). [ Example:

void f() {
    // ...
    goto lx; // ill-formed: jump into scope of a
ly:
    X a = 1;
    // ...
lx:
    goto ly; // OK, jump implies destructor
             // call for a followed by construction
            // again immediately following label ly
}

—end example ]