CC++ goto——大聪明的挖坑能手,高手的清晰助手

CC++ goto——大聪明的挖坑能手,高手的清晰助手,第1张

关于goto语句的见解
  • 序言
  • goto为什么这么令人讨厌
    • 逻辑顺序混乱
    • 容易越过不能越的代码区
  • 使用goto的可行方案
    • 1、只在函数里使用
    • 2、尽量或最好只作为事件反馈功能
    • 3、为避免带偏见的人不爽,每个goto语句留下注释
    • 4、函数划分要划好逻辑块
  • 为什么要使用goto,有什么好处?
    • 指引统集return

序言

我其实对goto语句一直抱有一定的疑惑,我记得刚开始学C的时候,老师布置作业,是有含着多重循环的某个数组内容匹配查找,我当时灵光一闪,用了goto迅速从多重循环里跳出来到末尾打印结果,结果被老师骂了一顿。

为什么呢?即使以我现在的浅薄水平来看,依然并无任何的问题存在,也很清晰,大概是这样的,我忘了以前的了。

QString func()
{
    const int judgeValue = 3;
    const int array[2][4] = {{5,6,7,8},{1,2,3,4}};

    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            if (array[i][j] == judgeValue)
            {
                goto FIND_OUT;
            }
        }
    }

    return "ERROR";
FIND_OUT:
    qDebug()<<"find out value";

    return "OK";
}

说起老师的理由也是挺奇葩的,不是因为我写得不好,而是因为我用了goto语句!?

我最近问了一次群友对goto的看法,结果存在相当一部分是看到用了就骂,我十分不赞同,所以我写下这篇。

goto为什么这么令人讨厌

其实我认为工具的好坏取决于使用者,goto不可能是真的不好,其应该存在其该存在的理由,不然甚至都不会还保留着。

逻辑顺序混乱

下文程序来源于invalid s分享的basic程序,充斥着goto语句,这种情况下,如果不缩行,你看得有清晰条理可言吗?

作者:invalid s
链接:https://www.zhihu.com/question/20259336/answer/1779133478
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

10 let int i = 1
20 do sth
30 do other
40 goto 190
50 other
60 gosub 180
70 gosub 170
80 gosub 160
90 XX
100 goto 240
...
160 let int call_flag = 1
170 print("hello world!")
180 let int call_flag2 = 2
190 what the fuck?
200 if call_flag = 1 goto 90 '不return了,将来有伏笔!
210 if call_flag2 = 2 goto 230
220 goto 40  '只有第40行会直接goto 190,直接goto回去就好了
230 goto 70
240 do other
 '注意这里利用了“伏笔”,所以千万别直接goto 160~180,会崩!
250 if call_flag2 = 2 return '本质上,这是把主程序中的若干行代码拿来当函数体内代码用了,因此其中引用的部分变量可能来自函数内部;return后回归本源
260 print("still alive?")
270 goto 10

你能够很快理解其中的功能吗?你只能从开头顺着一步步往下看,跳来跳去,理解都费劲。

当然,C/C++的goto不支持行号,如果支持行号,随便一更改程序就会全部乱套了。

容易越过不能越的代码区

最简单的,我随便写了一点程序代码,当代码足够多时,你可能会想着直接跳到最后返回,一般来说做法挺好的,但是如果存在一些不能跳的代码你忽略了呢?或许你会觉得这么简单的不可能忽略呢?

不要小看人的遗忘能力,看了信息忘了回都是常有的 *** 作,况且我也只是简单的写一下代码。

23 {
24		Test memory = new Test();
		...
27 		for (...; ...; ...)
28		{
29			goto PACKAGE_ERROR;
30		}
		...
57 		if (...)
56 			delete memory;	//跳过了!导致内存泄漏!!!!!
		...
85 PACKAGE_ERROR:
86 		return "OK";
87 }

当某个功能需要编程跳到某行,可能就会直接想了一下,哦,用goto就行了,结果以前这写了什么代码也不太记得了,bug百家争鸣。
.

使用goto的可行方案

这是个人对goto的想法可行方案,非行业规范和标准,如有相似,实属同道中人。

使用goto时,要注意4点关键

1、只在函数里使用

这点很容易理解,当goto和标记在同一个函数里使用时,看这段代码,不需要顺着goto跳到其他地方看,会相对容易看一些,最好函数代码不要超过80行。

2、尽量或最好只作为事件反馈功能

比如说在某个位置判断出了事件所要知道的结果,已经不必要继续往下运行了,可以直接跳到函数的末尾进行函数收尾处理。

...
	if (array[i][j] == judgeValue)
	{
		goto FIND_OUT;
	}
...
FIND_OUT:
	qDebug()<<"find out value";
	//释放栈内存等 *** 作,或者调用一些比如FFmpeg的释放编解码器之类的函数
	return "OK";

其实也可以使用智能指针避免内存泄漏 *** 作。

3、为避免带偏见的人不爽,每个goto语句留下注释

如上,不喜欢用goto的,总会有所偏见,认为用goto会到处跳来跳去,对于这些人是没那么容易改变的,所以,最好写上注释,注明两部分:①为什么要跳;②跳去哪。

比如下面那点的代码,写清楚为什么要跳,要跳到末尾,自然就很快看到跳在哪里。

4、函数划分要划好逻辑块

我随便写一点函数,非正常使用功能函数,有些人喜欢按逻辑功能划块,有些人喜欢按代码功能划块。

都可以,但是一定要清晰明了,让人快速明白你的函数分布,不至于跳过一些不能跳的地方。

QString func()
{
    //第一部分,放定义块
    const int judgeValue = 3;
    const int array[2][4] = {{5,6,7,8},{1,2,3,4}};
    int *test_memory = new int(5);

    //第二部分,放函数代码逻辑块
    if (test_memory == nullptr)
    {
        goto MEMORY_NULL;		//申请内存失败(虽然此处申请无意义,但演示嘛),跳转末尾返回
    }
    
    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            if (array[i][j] == judgeValue)
            {
                goto FIND_OUT;	//找到该数,跳转至末尾返回
            }
        }
    }

    //第三部分,统一放返回值,函数结束时需解决的事件如内存释放等
    delete test_memory;
    return "can't find";
    
MEMORY_NULL:
    return "memroy is null";
    
FIND_OUT:
    delete test_memory;
    return "find out";
}

.

为什么要使用goto,有什么好处?

总的来说就是因为便捷,但是会有所影响可读性和带偏见人的骂欲。

虽然可以从繁杂的多重循环一次跳出来的好处功能,但是,你能使用繁杂多重循环自然也说明了你的逻辑有点杂了,无论用不用goto,其实你都可以有方法解决优化的,所以不要轻易使用goto,除非你不在乎多重循环里可能存在的数据处理功能。

goto是一种捷径,这会很快写好混乱的代码,即便这是你以为的清晰,正常的代码也足以构建好很好的代码。

犹如建造高楼,goto在正常逻辑处理中相当于一个长木棍,你通过它爬上了天空,却以为你已经完成了高楼。

所以,为了可读性,最好只像上面那种用法,统一管理返回,而尽量或最好不要在逻辑处理中去使用goto语句。

指引统集return

每个函数都有return,长长的函数中可能有N次的判断返回,很容易不知道哪里就返回了,因为什么返回。所以使用goto统一在末尾返回,可以清楚得看到返回的地方和原因,返回的条件,这是一个便于读代码的好方法。

注:为什么我不说try catch捕获异常用goto的情况呢?因为我觉得try catch捕获自己定义的“异常”属实有点鸡肋,所以我一般不用它。

完~有错误望请指正谢谢

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/1330165.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-12
下一篇2022-06-12

发表评论

登录后才能评论

评论列表(0条)

    保存