看到,网上很多人对于goto的询问, 因为本身在工作中经常使用到,所以写下此文, 如有错误, 请指出.

本人写博文的时候主要从事C++工作

对于goto的态度,本人目前成长如下:

学生时代

老师课堂上说,goto语句容易把程序的顺序逻辑结构扰乱. 由于是学生, 所以你懂的. 听老师的, 而且自己网上看了, 多数人也是反对使用goto的.备注:学生时代, 你也懂的, 根本没有考虑工程性. 很少使用goto, 也没有goto发挥的余地.

我的师傅

工作以后的,第一位师傅, 三星十年C语言图像算法工程师. 教育我说, 要学会在程序中使用goto. 不理解, 和她讨论了以前老师的一些想法.她当时举例大概如下:


#define FREE(p) {if(NULL!=(p){free(p);(p)=NULL;}} void fun()
{
int *p = NULL;
int *p1 = NULL;
int *p2 = NULL; p = (int *)malloc(sizeof(int) * 10);
if( NULL == p ){goto _END;}
p1 = (int *)malloc(sizeof(int) * 10);
if( NULL == p1 ){goto _END;}
p2 = (int *)malloc(sizeof(int) * 10);
if( NULL == p2 ){goto _END;} _END:
FREE(p);
FREE(p1);
FREE(p2);
}

当时,我也没有太理解. 目前理解了. 师傅是C语言,经常和内存打交道. 师傅当时的解释:对比上下代码


#define FREE(p) {if(NULL!=(p){free(p);(p)=NULL;}} void fun()
{
int *p = NULL;
int *p1 = NULL;
int *p2 = NULL; p = (int *)malloc(sizeof(int) * 10);
if( NULL == p )
{
return;
}
p1 = (int *)malloc(sizeof(int) * 10);
if( NULL == p1 )
{
FREE(p);
return;
}
p2 = (int *)malloc(sizeof(int) * 10);
if( NULL == p2 )
{
FREE(p);
FREE(p1);
return;
} /*...*/
}

也许你会认为这只是几个malloc, 你太天真了. 其实对于她们C开发, 有的时候, 函数体会很长, 而期间有一些函数会出错, 对于出错了, 怎么办? 使用goto, 来协定一个错误处理机制, 错误的处理, 也就是例子中内存的回收,统一放在函数的尾部, 不容易遗漏. 一旦某个地方出错了, 直接返回尾部即可.这是在调用malloc函数的时候, 其实, 自己写的函数, 也并不一定总是返回正确的结果. 那么如果函数一层一层嵌套的比较深了, 一个统一的错误处理机制是非常重要的, 尤其是在团队开发的时候. 目前,我所在的团队, 都是按照这个标准. 我们团队函数的基本模型如下:


int foo(int *p)
{
int nRet = -1; /** goto _END; */ if( 0 > foo1() ){goto _END;} nRet = 1;//只有当程序运行到底部,这里的时候, 这个函数才属于正常的运行完毕, 中间有任何的错误, 就会goto跳过这一步.
_END:
return nRet;
} //那么,我们来一次深层嵌套 int main()
{
int nRet = -1; if( 0 > foo() ){goto _END;} _END:
return nRet;
}

我这里仅仅采用了三层函数嵌套, 其实试想一下, 往往开发中, 我们会发现, 函数嵌套, 会在不知不觉中, 让我们都蛋疼的事情.

我的使用

师傅是C语言, 当时列举的例子是和内存相关的. 而我工作中主要是C++, 我们知道C++有new 和 delete, 这两个函数是相对于C的malloc 是比较安全. 由于目光短浅, 师傅的强制要求, 自己心里还有些不爽, 甚至和师傅进行了一次激烈的讨论. 因为我没有按照师傅的来. 师傅在检查我代码的时候, 批评了好几次. 拿自己的天真挑战师傅的经验. 肯定是失败的.

在慢慢的使用过程中, 我才体会到goto的强大魅力.有的时候,我们在程序中, 会有这样的逻辑.


int foo()
{
if( 条件1 )
{
if( 条件2 )
{
if( 条件3 )
{
/** ... */
}
}
else if( 条件4 )
{ }else
{
/**...*/
}
}
else if( 条件4 )
{
/**...*/
}
}

对于这样的程序逻辑, 你觉得可读性很强吗? 对于程序中的if else, 我是可笑又可恨, 我记得有些人甚至批判过if else, 能把你的思路绕晕了. if else的深层嵌套, 在goto这里, 可以优化成一层, 将其扁平化处理

int foo()
{
int nRet = -1; if( 条件1 )
{
/** do some thing */
goto _OK;
} if( 条件2 )
{
/** do some thing */
goto _FAILED
} if( 条件3 )
{
/** do some thing */
goto _OK;
} if( 条件4 )
{
/** do some thing */
goto _OK;
} _OK:
nRet = 1; _FAILED:
return nRet;
}

上下两部分不能完全对应, 我只是举个例子.也就是说, goto可以处理复杂的if else.

使用goto注意事项

上面两个goto例子, 一个是师傅经常使用的, 一个是我慢慢体会到的. 当然了,师傅在上.goto很灵活, 会用的人, 能把goto的威力发挥出来, 就想只有孙悟空才可以发挥金箍棒的威力一样. 使用过程中, 需注意如下:

  1. 细心的应该发现, 我们所使用的地方,都是在一个函数内部.也就是说, goto, 只在函数内部,** 千万千万千万别goto到其他函数内部. **
  2. 使用goto, 编译器有时候, 会报出变量定义问题. 在一个代码作用域中, 所有变量的声明定义必须在第一个goto的前面. 我们团队一般要求,统一函数头部. 注意作用域的理解.
int foo()
{
int nRet = -1; if( 条件1 )
{
/** do some thing */
goto _OK;
} int num;//报错, 应该移动到前面
if( 条件2 )
{
/** do some thing */
goto _FAILED
} if( 条件3 )
{
int num3;//不报错, 因为在{}这个作用域, 是在goto的前面
/** do some thing */
goto _OK;
} if( 条件4 )
{
/** do some thing */
goto _OK;
} _OK:
nRet = 1; _FAILED:
return nRet;

坚定使用goto

体会到了goto的魅力, 我还没有坚定我的信念,知道我碰到了一些远古级别的代码的时候, 我笑了, 他们也在使用goto.

自我评鉴goto

这个世界上, 总是存在这么一个现象, 有人说好, 必定有人说坏. 说好的人能列举一大堆好的例子, 不好的依然. 对于goto,我想说, 会用的, 把他用好, 不会用的. 可以使用自己认为好的方法. 方法有很多, 我们的目的只有一个, 写出安全的代码, 和清晰的程序逻辑. 只要能达到这个目标, 什么方法都可以.

goto使用总结

  1. 团队开发协定函数的错误反馈机制
  2. goto处理if else的多层嵌套, 将其扁平化处理.

How To Use Goto?的更多相关文章

  1. 因为没用过,所以没想过的--goto

    今天读了读 Rui Maciel 大神写的 mjson parser,mjson 解析器是一个使用 ISO C 实现的小型 JSON 解析器.嵌入式项目中使用到了该解析器,随即拿出来看看. 看到如下代 ...

  2. bat脚本参数 if goto choice for使用的学习笔记。

    写过几次bat脚本,但一直没有总结,最近找到一个网页介绍bat,总结得很好,转自 http://www.jb51.net/article/49627.htm: 本文只总结我不会的,全面的看原网页就可以 ...

  3. 用goto做异常处理

    http://www.cnblogs.com/trying/archive/2012/06/25/2863753.html 今天在CSDN上看到的关于错误返回值的讨论,感觉非常有趣. 从中可以看出被教 ...

  4. 尽量用goto代替尾递归

    void PrintList(List L) { if(L!=Null) { PrintElement(L->Element); PrintLisr(L->Next); } } 所谓尾递归 ...

  5. C++:为什么说 goto 没有用

    要了解一个功能有没有用,首先应该分析它能实现的所有功能. goto 可以实现的功能只有两种:一,向前面跳:二,向后面跳.这两种情况对应三种功能:一,重复执行也就是循环:二,跳过一段代码也就是条件判断: ...

  6. C语言的傻瓜式随笔(二):全局变量、预编译、goto

    函数的作用:可以实现代码的重用. 函数只需要定义1次,那么函数中的代码就可以随意的调用.       -某不知出处的基本概念 学而时习之,如有误笔,请指正 一.goto跳转语句 goto在C语言的作用 ...

  7. GOTO Berlin: Web API设计原则

    在邮件列表和讨论区中有很多与REST和Web API相关的讨论,下面仅是我个人对这些问题的一些见解,并没有绝对的真理,InnoQ的首席顾问Oliver Wolf在GOTO Berlin大会上开始自己的 ...

  8. 辗转相除法求最大公约数,非goto

    #include<iostream> using namespace std; //不推荐用goto,当然用它更快 //辗转相除法求两数的最大公约数 int gcd(long int a, ...

  9. C语言字符串匹配、goto语句、关机命令使用

    1.程序执行修改窗口字体颜色命令: 2.程序执行修改窗口标题命令: 3.程序执行关机倒计时命令: 4.根据提示输入团队名称JYHACK TEAM 根据提示输入团队网址:http://bbs.jyhac ...

随机推荐

  1. 对于挑战书上的很久之前都看不懂的DP看懂的突破

    突破一..牢记问题概念 并且牢记dp状态方程 突破二..一直有一个求和dp转化成O1dp递推的式子看不懂.. 看不懂的原因是..没有分清求和符号作用的范围 提醒:以后遇到求和符号一定明确其求和的式子的 ...

  2. (转)Lock和synchronized比较详解

    今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉,有许多疑问,所有在网上找了很多文章看了一下,总体说的不够详细,重点和焦点问题没有谈到,但这篇文章相当不 ...

  3. Linux Shell 流程控制语句

    * 本文主要介绍一些Linux Shell 常用的流程控制语句* 1. if 条件语句:if-then/if-elif-fi/if- else-fi if [条件判断逻辑1];then command ...

  4. C++常用特性原理解析

    在我的早期印象中,C++这门语言是软件工程发展过程中,出于对面向对象语言级支持不可或缺的情况下,一群曾经信誓旦旦想要用C统治宇宙的极客们妥协出来的一个高性能怪咖. 它驳杂万分,但引人入胜,出于多(mi ...

  5. linux中shell变量$#,$@,$0,$1,$2的含义解释

    linux中shell变量$#,$@,$0,$1,$2的含义解释: 变量说明: $$ Shell本身的PID(ProcessID) $! Shell最后运行的后台Process的PID $? 最后运行 ...

  6. BZOJ4596: [Shoi2016]黑暗前的幻想乡

    Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪 ...

  7. mac 终端常用命令

    1.复制文件内容到剪贴板:pbcopy < ~/.ssh/id_rsa.pub. 2.ssh key 的生成,参考mac ssh key 的获取. 3.sourcetree 需要输入的密码,指的 ...

  8. Mariadb数据库设置及操作 一主多从 备份还原(实测笔记)

    环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G,双网卡) 系统版本:CentOS-7-x86_64-Minimal-1611.iso 数据库版本信息 : 10.1.20- ...

  9. java-sql注入攻击

    注射式攻击的原理 SQL注射能使攻击者绕过认证机制,完全控制远程服务器上的数据库.SQL是结构化查询语言的简称,它是访问数据库的事实标准.目前,大多数Web应用都使用SQL数据库来存放应用程序的数据. ...

  10. Oracle CDC配置案例

    异步部署 1. 环境的配置准备 1.1.    数据库版本 SQL> select * from v$version; BANNER ------------------------------ ...