语句goto不能跨变量定义?

时间:2013-01-11 08:25:21

标签: g++ goto

假设这些代码在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之间的变量 - 跨区域,初始化它们(并且仍违反某些原则)。

2 个答案:

答案 0 :(得分:32)

Goto不能跳过变量的初始化,因为跳转后各个对象不存在,因为执行初始化时会启动具有非平凡初始化的对象的生命周期:

C ++11§3.8/ 1:

  

[...]类型为T的对象的生命周期始于:

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

  •   
  • 如果对象具有非平凡的初始化,则其初始化完成。

  •   

C ++11§6.7/ 3:

  

可以转换为块,但不能以初始化绕过声明的方式。一个   程序从具有自动存储持续时间的变量不在范围内的点跳转到a   除非变量具有标量类型,具有普通默认值的类类型,否则它在范围内的点是不正确的   构造函数和一个普通的析构函数,这些类型之一的cv限定版本,或其中一个的数组   在没有初始化器(8.5)的情况下声明前面的类型。

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

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

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

完全有效。

另一方面,该文件的扩展名为.c,表明它是C而不是C ++。如果使用gcc而不是g++编译它,原始版本应该编译,因为C没有这个限制(它只对可变长度数组有限制 - 在C ++中不存在在所有)。

答案 1 :(得分:2)

对于像int这样的原始类型,有一种简单的解决方法:

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

 // ---  work-around form: no more cross initialization error.  ---
 int foo;  foo = 0;