假设以下代码在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之间的变量,不要初始化它们(仍然违反一些原则)。
Goto不能跳过变量的初始化,因为相应的对象在跳转后将不存在,因为具有非平凡初始化的对象的生命周期从初始化执行时开始:
c++ 11§3.8/1:
[…]类型T对象的生命周期从以下时刻开始:
为T型获得正确对齐和大小的存储器,
如果对象具有非平凡初始化,则表示初始化完成。
c++ 11§6.7/3:
可以传输到块中,但不能通过初始化绕过声明。一个从具有自动存储持续时间的变量不在作用域中的点跳转到它在作用域中的点是病态的,除非变量具有标量类型,类类型具有简单的默认值构造函数和普通析构函数,这些类型之一的cv限定版本,或前面的类型和声明没有初始化式(8.5)
由于错误提到了[-fpermissive]
,您可以通过指定编译器标志将其转换为警告。这说明了两件事。它曾经是允许的(变量将存在,但在跳转后未初始化),并且gcc开发人员认为规范禁止它。
编译器只检查变量是否应该初始化,而不检查它是否被使用,否则结果会相当不一致。但是,如果你不再需要这个变量,你可以自己结束它的生命周期,使"尾部结束"成为可能:
int main() {
int a =0;
goto exit;
{
int *b = NULL;
}
exit:
return 0;
}
是完全有效的。
附带说明,该文件的扩展名为.c
,这表明它是C而不是c++。如果你用gcc
而不是g++
来编译它,原始版本应该可以编译,因为C没有这个限制(它只对可变长度数组有限制——这在c++中根本不存在)。
对于像int
:
// --- original form, subject to cross initialization error. ---
// int foo = 0;
// --- work-around form: no more cross initialization error. ---
int foo; foo = 0;