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

本文讨论了编程中的goto语句,指出其可能导致逻辑顺序混乱和潜在的错误,如内存泄漏。提出了使用goto的四个注意事项,包括限制在函数内使用,仅用于事件反馈,每个goto留注释,以及良好的函数逻辑划分。尽管goto提供了一定的便捷性,但应谨慎使用以保持代码可读性。建议在必须跳出复杂循环时考虑使用,但要避免在逻辑处理中滥用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

序言

我其实对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捕获自己定义的“异常”属实有点鸡肋,所以我一般不用它。

完~有错误望请指正谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

溪渣渣_梁世华

打赏?我甚至没有任何收费的章节

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值