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