语句goto不能跨变量定义?

问题描述 投票:22回答:2

假设这些代码在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++禁止有用的尾部声明声明的起源设计考虑因素是什么?


更新:

goto可以交叉变量(任何类型的变量,不限于指针)声明,但除了那些获得初始化值。如果我们删除上面的NULL作业,g++现在保持沉默。因此,如果您想声明goto-cross-area之间的变量,请不要初始化它们(并且仍然违反某些原则)。

g++ goto
2个回答
32
投票

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

C ++11§3.8/ 1:

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

  • 获得具有适当对齐和T型尺寸的存储,并且
  • 如果对象具有非平凡的初始化,则其初始化完成。

C ++11§6.7/ 3:

可以转换为块,但不能以初始化绕过声明的方式。从具有自动存储持续时间的变量不在范围内的点跳转到其在范围内的点的程序是不正确的,除非变量具有标量类型,具有普通默认构造函数的类类型和普通的析构函数,这些类型之一的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 ++中根本不存在)。


2
投票

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

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

 // ---  work-around form: no more cross initialization error.  ---
 int foo;  foo = 0;
© www.soinside.com 2019 - 2024. All rights reserved.