哈,说下为什么我喜欢c语言中间用goto

中山野鬼 发布于 2014/08/04 00:16
阅读 3K+
收藏 3

先上段代码,这个代码的作用是对P 里面存储的数据进行处理。如果发现\n就替换为0,如果发现\0则表示后续数据无意义,返回实际存在多少个完整字符串或完整的行。实际目的是每次我们从文件中读取文本,按照行为单位转化为字符串。这个P里的数据结构是循环队列。

static _I txtbuf_adj_f2m(_P P){
	_I re = 0;
	_u8 * p = (_u8 *)_Pp(P);
	_I ri;
	_I cut;
	_abort_n(_SIZE(P) != 1,gs_files_jmpt,_P_SIZE_ERR);
	if (_que_empty(P)){
		goto _txtbuf_adj_f2m_END;
	}
	ri = _HEAD(P);
	if (_HEAD(P) >= _TAIL(P)){
		while (ri < _NUM(P)){
			if (p[ri] == _STR_TAIL){
				cut = _USED(P) - ri - _HEAD(P);
				goto _txtbuf_adj_f2m_ERR;
			}
			if (p[ri] == _LINE_TAIL){
				p[ri] = _STR_TAIL;
				re++;
			}
			ri++;
		}
		ri = 0;
	}
	while (ri < _TAIL(P)){
		if (p[ri] == _STR_TAIL){
			cut = _TAIL(P) - ri;
			goto _txtbuf_adj_f2m_ERR;
		}
		if (p[ri] == _LINE_TAIL){
			p[ri] = _STR_TAIL;
			re++;
		}
		ri++;
	}	

_txtbuf_adj_f2m_END:	
//	_si(_USED(P));
	return re;
_txtbuf_adj_f2m_ERR:	
	_que_unload(P,cut);
	goto _txtbuf_adj_f2m_END;	
}



哈,说下c语言里面用goto的几点好处。

1、主逻辑先写,再处理异常,比如

if (_que_empty(P)){
		goto _txtbuf_adj_f2m_END;
	}
if (p[ri] == _STR_TAIL){
			cut = _TAIL(P) - ri;
			goto _txtbuf_adj_f2m_ERR;
		}
这两段代码都是边界条件下的处理。不影响主逻辑的代码结构,用goto是最方面不过的。而且看的时候也很清晰。这里的主逻辑,就是从_HEAD(P)到_TAIL(P)扫描而已。

2、分支与汇聚。不同类别的情况我们当然分支,相同类别的情况我们当然要汇聚,可惜进入相同类别的各个入口并不一定如你所愿的在主逻辑中集中。不用goto用什么?直接return ?,这会引发两个问题。

2.1 问题1,监测点的问题

_txtbuf_adj_f2m_END:	
//	_si(_USED(P));
	return re;
_txtbuf_adj_f2m_ERR:	
	_que_unload(P,cut);
	goto _txtbuf_adj_f2m_END;	
}
原本这样写的。。
_txtbuf_adj_f2m_END:	
//	_si(_USED(P));
	return re;
_txtbuf_adj_f2m_ERR:	
	_que_unload(P,cut);
	return re;	
}
_si(_USED(P)); 是个debug宏,会在DEBUG宏开的时候,把后面的信息打印到屏幕同时写入bug文件。_USED(P)表示P这个循环队列里总共有使用了几个存储单元。

结果抓bug呢?漏下了下面的_si(_USED(P));,哈,某个分支没有监测到。

2.2 问题2 ,逻辑调整的问题

有两处goto XX_ERR;

if (p[ri] == _STR_TAIL){
				cut = _USED(P) - ri - _HEAD(P);
				goto _txtbuf_adj_f2m_ERR;
			}
if (p[ri] == _STR_TAIL){
			cut = _TAIL(P) - ri;
			goto _txtbuf_adj_f2m_ERR;
		}
我相信反goto派会建议这么写。。。
if (p[ri] == _STR_TAIL){
			cut = _TAIL(P) - ri;
		       _que_unload(_USED(P)-cut);
return;
		}
好吧,在你证明你的方法很 有效的时候,你忽视了另一个地方的调整。哈。

或许反goto派会说,这个逻辑这么简单,怎么可能忘了两个地方都要处理后再return 。妹的,你过1年打算调整代码逻辑了,你怎么保证相同异常处理的分支工作在不同发生的地方能一次性调整到位?为何不规规矩矩的都集中起来?

说来说去,c语言的goto的必要性,和c语言是过程化的设计有关联。所谓过程化,其实和代码版本控制很像。你首先有个主版本,然后有分支,最后又需要汇聚。主逻辑干主逻辑的事情,if ,while ,for ,do while ,switch 等等为完整的逻辑服务,goto用于异常的转移,按照目标汇聚的原则,将不同转移入口集中到一块。哈。如同_abort_n所做的事情。这个宏的实际定义如下,是个长跳转:

#define _abort_n(exp,jmpbuf,n) do {if (exp){\
extern jmp_buf jmpbuf;\
gs_errno = (_I)(n);\
_gs_delog(_ABORT_INFO_DELOG,__FILE__,__LINE__,__func__,mkstr(n));\
		longjmp(jmpbuf,_ERR_INFO_DELOG);\
	}}while (0)



过程化的语言和面向对象的语言有很大的不同。过程化的语言,需要尽情的用goto跳来跳去。当然不是群魔乱舞,跳是为了集中的跳到指定的坑里,而不是跳完了都不知道跳哪了。哈。

注意哦,我是说与面向对象的语言差异,不是面向对象的分析,c语言的设计也需要基于面向对象的分析和面向环境(设备、操作系统,资源)的分析。

你不擅长用goto,说明你的c语言还停留在课堂。哈。只有那些没有工程经验的老师和同时在忙面向对象语言开发和过程化语言开发的大脑混乱的工程师,才反对你使用goto。


补充一句,特地贴段代码上来说事,就怕所谓“学者”和所谓“老师”扯淡。喷我的,不妨针对上面的代码按照不用goto的方式写一遍,让这里的小朋友(我想总该有的),看看。看看哪种写法更符合阅读,更有结构性。记得,边界检测。当然我这少了一个对P == 0 的检测,也很简单 ,最上面增加一句

_abort_n(P==0,gs_files_jmpt,_P_NULL_ERR);


加载中
7
修改登录密码
修改登录密码

反对本文观点. 尤其是最后一句坚决反对,会把很多新人引入坑, 让他们误以为频繁使用goto是C高手的标志

文中提出的例子其实很容易用无goto语句解决,而且不会降低可读性和可维护性。

反对goto的原因并不是擅长goto与否, 而是goto语句破坏了代码的结构性,

给代码优化和代码分析带来障碍, 为了一点点地提升付出了太多的代价,不值得

尤其是滥用goto的代码几乎无法维护

只有特别复杂的逻辑结构里用goto能极大简化代码结构才使用它,比如从一个深层的嵌套控制中跳转出来会大大简化代码才谨慎使用goto


弦歌
弦歌
这个没法说,就像到了乔峰的境界,随便一套入门的罗汉拳也能打败高手,但你跟初学者说让他只学罗汉拳就能打败高手,这不是笑话吗?任何论点都离不开语境,所以有时候这种辩论都是在用火星的语言来辩论地球的问题。
young7
young7
幸好你是一楼,要是被舔菊帝们占据一楼,估计又要“毁人不倦”了
2
0xAB
0xAB
奶奶的,程序代码变量命名也太坑了.
1
中山野鬼
中山野鬼

引用来自“eel”的评论

反对本文观点. 尤其是最后一句坚决反对,会把很多新人引入坑, 让他们误以为频繁使用goto是C高手的标志

文中提出的例子其实很容易用无goto语句解决,而且不会降低可读性和可维护性。

反对goto的原因并不是擅长goto与否, 而是goto语句破坏了代码的结构性,

给代码优化和代码分析带来障碍, 为了一点点地提升付出了太多的代价,不值得

尤其是滥用goto的代码几乎无法维护

只有特别复杂的逻辑结构里用goto能极大简化代码结构才使用它,比如从一个深层的嵌套控制中跳转出来会大大简化代码才谨慎使用goto


哈,你仔细看看我在说什么哦。 主逻辑不使用goto,goto产生的原因是,主逻辑和边界检测和异常处理需要集中汇聚完成。这是两件事情。看来你写代码中对异常或者边界处理的情况比较少。要写多的,或许就理解我所谓的“主逻辑”和应该使用goto的逻辑。

goto破坏代码的结构性只是不知道goto该怎么用的人的臆想。哈。我的观点中,应该用goto不是为了当前的优化,相反是为了保证代码结构性。包括长跳,longjmp的使用。

当然有一点我认同,对于新手,先学好书本上的基本知识吧。真进入业务场景是另外一回事。

1
纠结名字
后面一定还有人跪舔,你们基本看到帖子作者就知道该怎么回复了,对吗?
young7
young7
粉丝固然是在拜神,但你没看出来他是相当享受被舔么?哪天闲得蛋疼了就出来哈一下什么的,再故意选一个标新立异的话题,然后就站在一边作高人状了,不时指点一下江山说下这个不行那个片面,最终还是老子最diao,存在感得到了充分满足,呵呵。
中山野鬼
中山野鬼
回复 @young7 : 嵌入式,现在还有几个在真正做嵌入式?不过话说那个圈子的朋友不少,我也算嵌入式行业干过几年专业的事情。不过现在忙服务器上的数据治理,爱在哪就在哪。哈。
此号作废
此号作废
回复 @young7 : c里程序异常,你是怎么释放资源的。说一下你的见地?我反正用的是goto。
young7
young7
呵呵,大实话。常见的模式是半桶水兄出来误导众人,然后一帮舔菊帝跟上。我说他要是觉得自己高大上就去国内嵌入式论坛,他来这个企业级开发晒一下自己那点底层知识,是为了寻找遗失的优越感么?
young7
young7
别纠结名字了
1
htfy96
htfy96
我记得有一种折中的认识是,在某一个大代码块中,goto尽量朝着一个方向使用,如果有的向上有的向下就要考虑改变结构了
0
呆萌
呆萌

中山野鬼是野路子练出来的民科?

Dijkstra这些人做出的贡献让你啪啪耳光扇到死啊。图灵奖选手在你这里是没经验的老师还是大脑混乱的工程师啊?你学过汇编吗?控制语句跟gote语句的汇编码你看过吗?野路子写了几年代码,发现了一点奇技淫巧,轻浮的容不下自己了?

落英缤纷
落英缤纷
就事论事啊 你这样真没意思
0
中山野鬼
中山野鬼

引用来自“呆萌”的评论

中山野鬼是野路子练出来的民科?

Dijkstra这些人做出的贡献让你啪啪耳光扇到死啊。图灵奖选手在你这里是没经验的老师还是大脑混乱的工程师啊?你学过汇编吗?控制语句跟gote语句的汇编码你看过吗?野路子写了几年代码,发现了一点奇技淫巧,轻浮的容不下自己了?

不好意思,c语言之前,规规矩矩学和用了3年pascal,结构化的编程一直按照套路来,不存在野路子。c语言也算实打实的写了18年。至于汇编嘛,51,avr,arm ,dsp,也算写了不少。包括驱动和算法优化。包括MPEG-1,2,4,H264,REAL的核心解码算法,70%使用dsp或arm汇编,如果野套路,抓bug能抓死自己。建议你多写写工程代码吧。

至于什么是野什么不是野,我的观点,符合工程化开发,有利于团队开发,有可扩展、优化、延展的代码设计方法是科学的,反之是野的。包括书本和所谓“学者”扯淡的言论。

0
opal
opal
好多人是教条主义,书上怎么写,就怎么用,迷信外国大师,没有自己的思考,不会积累自己的经验,奴性十足
pendows
pendows
看来这位是奴性十足的跪舔国产砖家在非教条主义的教育制度下终于学会“独立思考”了,不容易啊,不过这回倒时没看到楼主咬牙切齿的声称“人家最最痛恨啥的搞地域攻击了”反倒好象是被舔的很受用,哈哈真有意思。
0
渔樵耕读
渔樵耕读
哈哈哈 沙发和3L是约好的么?
0
mingshun
mingshun

帖子要说的重点应该是这句吧:主逻辑干主逻辑的事情,if ,while ,for ,do while ,switch 等等为完整的逻辑服务,goto用于异常的转移,按照目标汇聚的原则,将不同转移入口集中到一块。

返回顶部
顶部