4

以下示例演示了该问题:

#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;
        }
}

最初,“剩余”变量的初始化有点长,我曾经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
$1 = 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不允许和可变参数数组组合,应该有警告吗?

4

1 回答 1

5

Variable length arrays(VLA) are a C99 feature that gcc supports as an extension in C++ and in C99 a jump across a VLA declaration is undefined behavior, from the draft C99 standard section 6.8.6.1 The goto statement:

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.

clang and gcc 4.9 actually makes this an error and says:

error: goto into protected scope
    goto loop;
    ^

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

See the gcc bug report: Jumps into VLA or VM scope not rejected for C++.

The rules for jumping past a declaration of an automatic variable in C++ is covered in section 6.7 [stmt.dcl] which says:

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 ]

于 2015-03-18T14:06:47.280 回答