32

假设这些代码编译在g++

#include <stdlib.h>

int main() {
    int a =0;

    goto exit;

    int *b = NULL;

exit:
    return 0;
}

g++会抛出错误:

goto_test.c:10:1: error: jump to label ‘exit’ [-fpermissive]
goto_test.c:6:10: error:   from here [-fpermissive]
goto_test.c:8:10: error:   crosses initialization of ‘int* b’

似乎goto不能交叉指针定义,但可以gcc编译它们,没有任何抱怨。

修复错误后,我们必须在任何指针之前声明所有指针goto语句之前声明所有指针,也就是说,即使您目前不需要它们,也必须声明这些指针(并且违反某些原则)。

g++禁止有用的tail-goto语句的起源设计考虑是什么?


更新:

goto可以跨变量(任何类型的变量,不限于指针)声明,但获得初始化值的除外。如果我们删除NULL上面的分配,g++现在保持沉默。所以如果你想声明goto-cross-area之间的变量,不要初始化它们(仍然违反一些原则)。

4

2 回答 2

42

Goto 不能跳过变量的初始化,因为在跳转后相应的对象将不存在,因为具有非平凡初始化的对象的生命周期在执行初始化时开始:

C++11 §3.8/1:

[…] T 类型对象的生命周期开始于:

  • 获得具有适合类型 T 的对齐和大小的存储,并且

  • 如果对象有非平凡的初始化,它的初始化就完成了。

C++11 §6.7/3:

可以转移到一个块中,但不能通过初始化绕过声明的方式。除非变量具有标量类型、具有普通默认构造函数和普通析构函数的类类型、以及这些类型之一的 cv 限定版本,或上述类型之一的数组,并且在没有初始化程序 (8.5) 的情况下声明。

由于错误提到[-fpermissive],您可以通过指定该编译器标志将其变为警告。这表明两件事。它曾经是允许的(变量会存在,但在跳转后未初始化)并且 gcc 开发人员认为规范禁止它。

编译器只检查变量是否应该被初始化,而不检查它是否被使用,否则结果会很不一致。但是,如果您不再需要该变量,您可以自己结束它的生命周期,使“tail-goto”变得可行:

int main() {
    int a =0;
    goto exit;
    {
        int *b = NULL;
    }
exit:
    return 0;
}

是完全有效的。

在旁注中,该文件具有扩展名.c,这表明它是 C 而不是 C++。如果你用gcc代替编译它g++,原始版本应该可以编译,因为 C 没有这个限制(它只有可变长度数组的限制——这在 C++ 中根本不存在)。

于 2013-01-11T08:31:04.537 回答
4

这些原始类型有一个简单的解决方法,例如int

 // ---  original form, subject to cross initialization error.  ---
 // int foo = 0;

 // ---  work-around form: no more cross initialization error.  ---
 int foo;  foo = 0;
于 2015-09-02T11:04:53.700 回答