假设这些代码编译成g++:
#include <stdlib.h>
int main() {
int a =0;
goto exit;
int *b = NULL;
exit:
return 0;
}
Run Code Online (Sandbox Code Playgroud)
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’
Run Code Online (Sandbox Code Playgroud)
似乎goto无法跨越指针定义,但gcc编译好,没有任何抱怨.
修复错误之后,我们必须在任何goto语句之前声明所有指针,也就是说你必须声明这些指针,即使你现在不需要它们(并且违反了一些原则).
什么原因设计考虑g++禁止有用的尾部goto声明?
更新:
goto可以交叉变量(任何类型的变量,不限于指针)声明,但除了那些获得初始化值.如果我们删除NULL上面的任务,g++请立即保持沉默.因此,如果要声明goto-cross-area 之间的变量,请不要初始化它们(并且仍然违反某些原则).
Jan*_*dec 33
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;
}
Run Code Online (Sandbox Code Playgroud)
完全有效.
另外,文件有扩展名.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;
Run Code Online (Sandbox Code Playgroud)