C++跳转语句之Goto对变量定义的影响详解

 更新时间:2020年4月25日 17:33  点击:2282

前言

goto语句也称为无条件转移语句,其基本形式如下 :

语句标号由一个有效地标识符和符号";"组成,其中,标识符的命名规则与变量名称相同,即由字母、数字和下划线组成,且第一个字符必须是字母或下划线。执行goto语句后,程序就会跳转到语句标号处,并执行其后的语句。

通常goto语句与if条件语句连用,但是,goto语句在给程序带来灵活性的同时,也会使得使程序结构层次不清,而且不易读,所以要合理运用该语句。

发现问题

我们经常碰到有在goto后面定义变量,linux下编译不通过的问题(报错信息:crosses initialization of)。其实,只要注意一下就好了,今天问了一下公司前辈之后,也翻了些资料,记录一下,加深记忆,也希望能对一些人有些许帮助。

错误示例代码:

#include <iostream>
using namespace std;

int main()
{
 goto Exit;
 int a = 0;
Exit:
 return 0;
}

报错:

[root@localhost c-c++]# g++ goto_study.cpp 
goto_study.cpp: In function 'int main()':
goto_study.cpp:31: error: jump to label 'Exit'
goto_study.cpp:29: error: from here
goto_study.cpp:30: error: crosses initialization of 'int a'

正确写法

也不能说是正确的写法,只能说是编译OK的写法。

直接上代码:

写法一:

改变域,变成局部变量:

int main()
{
 goto Exit;
 {
 int a = 0;
 }
Exit:
 return 0;
}

写法二

神奇的写法:

int main()
{
 goto Exit;
 int a;
 a = 1;
Exit:
 cout << "a = " << a << endl;
 return 0;
}

关键是还可以访问!结果:

[root@localhost c-c++]# g++ goto_study.cpp 
[root@localhost c-c++]# ./a.out
a = 1259648

研究

神奇的写法

看到两个可以编译通过的写法之后,最纳闷的是写法二为毛可以编译通过,而且还能使用???

C++规定

参考[1][2]中提到了C++标准中的规定: > It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer.

意思是说:如果一个程序的执行路径从代码中的点A(某个局部变量x还未定义)跳到代码中另一点B(该局部变量x已定义,并且定义的时候有初始化),那么编译器会报错。这样的跳跃可以是由于执行goto语句,或者是switch-case造成的。所以,在写法二中a是int类型,是一个POD类型,并且没有初始化,所以编译通过。但是,很明显:如果去使用这个变量a的时候,结果是未知的,就像前辈说的,没有意义,还不如不支持!那如果只在局部使用,完全可以用花括号括起来!网上也有人说到,C++规范虽然没有明确说明这样是错误的,但是变量的域的规定其实是隐性说这种做法是不可取的,见参考[4]。

隐性说明

Goto can't skip over definitions of variables, because those variables would not exist after the jump, since lifetime of variable starts at the point of definition. The specification does not seem to explicitly mention goto must not do that, but it is implied in what is said about variable lifetime.

-fpermissive标记

参考[4]中提到,g++编译器默认是检查的,自己可以设置编译器的这个标记变成警告,未实践!!!

查了下资料-fpermissive标记的作用是: 把代码的语法错误作为警告,并继续编译进程,所以就安全起见,这个角度就不要想了,还是老老实实码砖!

POD类型

参考[3],按照上面C++规定的说法,只要是POD类型,并且没有初始化都是可以编译通过的。

看一段代码:

#include <iostream>
using namespace std;
class A{
public:
 // 注意:和B不同的是有构造和析构函数, 所以编译报错
 A(){}
 ~A(){}
 void testA(){
 cout << "A::test." << endl;
 }
};
class B{
public:
 void testB(){
 cout << "B::test." << endl;
 }
};
int main()
{
 goto Exit;
 // int a = 1; // windows ok.linux failed!
 //A classA; // failed:
 B classB; // success:
 classB.testB();
Exit:
 classB.testB();
 return 0;
}

结果:

[root@localhost c-c++]# g++ goto_study.cpp 
[root@localhost c-c++]# ./a.out
a = 1259648
B::test.

小结:

      1、以上代码在windows和linux下均编译通过和执行;

      2、A classA一句在windows和linux均编译不通过!因为A有构造和析构函数,不满足条件;

      3、至于int a = 1;这样的写法在windows(msvc)下面能够通过就与C++规范不符了,求解释!!!

以下是POD类型(还是看英文吧):

      1、int, char, wchar_t, bool, float, double是POD类型,这些类型的long/short and signed/unsigned版本也是;

      2、 指针(包括函数指针和成员指针)都是POD类型;

      3、enums枚举类型;

      4、POD的const和普通变量也都是;

      5、POD类型的class,struct和union也是。但要求所有的成员是public,并且没有基类,没有构造、析构函数和虚函数。静态成员在这些规则下也是。

总结

      1、最好不要用goto;

      2、goto后面不要跳过定义和初始化的变量,如果是POD类型可以先申明再定义,是不会编译报错的。但是不建议这么使用,可以看到如果执行语句跳过了赋值语句,那么变量的值是未知的,存在危险性;

      3、goto后面如果是局部的变量,可以用花括号括起来构成一个局部域,就安全了。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

参考

[1]Getting a bunch of crosses initialization error

[2]>switch case、goto对变量定义的影响

[3]>“POD type” in C++

[4]>Statement goto can not cross pointer definition?

[5]>error: jump to label ‘foo' crosses initialization of ‘bar'

[!--infotagslink--]

相关文章

  • C++ STL标准库std::vector的使用详解

    vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
  • PHP成员变量获取对比(类成员变量)

    下面本文章来给大家介绍在php中成员变量的一些对比了,文章举了四个例子在这例子中分别对不同成员变量进行测试与获取操作,下面一起来看看。 有如下4个代码示例,你认...2016-11-25
  • C++中取余运算的实现

    这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ string常用截取字符串方法

    这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • R语言 如何删除指定变量或对象

    这篇文章主要介绍了R语言删除指定变量或对象的操作方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-06
  • C++中四种加密算法之AES源代码

    本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
  • C++ 整数拆分方法详解

    整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
  • C++中 Sort函数详细解析

    这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
  • 解决vue的router组件component在import时不能使用变量问题

    这篇文章主要介绍了解决vue的router组件component在import时不能使用变量问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-27
  • C++万能库头文件在vs中的安装步骤(图文)

    这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • Vue select 绑定动态变量的实例讲解

    这篇文章主要介绍了Vue select 绑定动态变量的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-23
  • 深入理解PHP变量的值类型和引用类型

    在PHP中,大部分变量类型,如字符串,整型,浮点,数组等都是值类型的,而类和对象是引用类型,在使用的时候,需要注意这一点。看到网友在讨论PHP的&符号,要彻底理解它的用法,就有必要讨论一下变量的两种形式。PHP的变量在内存中是这样...2015-10-23
  • 详解C++ bitset用法

    这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 浅谈C++中的string 类型占几个字节

    本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
  • C++ Eigen库计算矩阵特征值及特征向量

    这篇文章主要为大家详细介绍了C++ Eigen库计算矩阵特征值及特征向量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • java中String类型变量的赋值问题介绍

    下面小编就为大家带来一篇java中String类型变量的赋值问题介绍。小编觉得挺不错的。现在分享给大家,给大家一个参考。...2016-03-28
  • C#不同类型的成员变量(字段)的默认值介绍

    虽然C#编译器为每个类型都设置了默认类型,但作为面向对象的设计原则,我们还是需要对变量进行正确的初始化。实际上这也是C#推荐的做法...2020-06-25
  • C++ pair的用法实例详解

    这篇文章主要介绍了C++ pair的用法实例详解的相关资料,需要的朋友可以参考下...2020-04-25