我一直有一个印象,如果可能的话,永远不要使用goto。
然而,在前几天阅读libavcodec(它是用C编写的)时,我惊讶地注意到它的多种用法。
在支持循环和函数的语言中使用goto是否有优势?如果有,为什么?请提供一个具体的例子,清楚地说明使用goto的理由。
我一直有一个印象,如果可能的话,永远不要使用goto。
然而,在前几天阅读libavcodec(它是用C编写的)时,我惊讶地注意到它的多种用法。
在支持循环和函数的语言中使用goto是否有优势?如果有,为什么?请提供一个具体的例子,清楚地说明使用goto的理由。
当前回答
使用“goto”使你的代码更易于阅读或运行得更快。只是不要让它把你的代码变成意大利面条。
其他回答
Everybody who is anti-goto cites, directly or indirectly, Edsger Dijkstra's GoTo Considered Harmful article to substantiate their position. Too bad Dijkstra's article has virtually nothing to do with the way goto statements are used these days and thus what the article says has little to no applicability to the modern programming scene. The goto-less meme verges now on a religion, right down to its scriptures dictated from on high, its high priests and the shunning (or worse) of perceived heretics.
让我们把Dijkstra的论文放在背景中,对这个问题有一些了解。
When Dijkstra wrote his paper the popular languages of the time were unstructured procedural ones like BASIC, FORTRAN (the earlier dialects) and various assembly languages. It was quite common for people using the higher-level languages to jump all over their code base in twisted, contorted threads of execution that gave rise to the term "spaghetti code". You can see this by hopping on over to the classic Trek game written by Mike Mayfield and trying to figure out how things work. Take a few moments to look that over.
这就是Dijkstra在1968年的论文中谴责的“毫无节制地使用go to语句”。这就是他所生活的环境,促使他写出了那篇论文。在你的代码中,在你喜欢的任何地方跳转的能力是他所批评和要求停止的。将其与C或其他更现代的语言中goto的弱功能进行比较简直是可笑的。
我已经能听到信徒们面对异教徒时扬起的呐喊声了。“但是,”他们会念叨,“用c语言的goto会让代码变得很难读。”哦,是吗?如果没有goto,代码也会变得难以阅读。比如这个:
#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_
_-_-_-_
}
看不见goto,所以它一定很容易读,对吧?或者这个怎么样:
a[900]; b;c;d=1 ;e=1;f; g;h;O; main(k,
l)char* *l;{g= atoi(* ++l); for(k=
0;k*k< g;b=k ++>>1) ;for(h= 0;h*h<=
g;++h); --h;c=( (h+=g>h *(h+1)) -1)>>1;
while(d <=g){ ++O;for (f=0;f< O&&d<=g
;++f)a[ b<<5|c] =d++,b+= e;for( f=0;f<O
&&d<=g; ++f)a[b <<5|c]= d++,c+= e;e= -e
;}for(c =0;c<h; ++c){ for(b=0 ;b<k;++
b){if(b <k/2)a[ b<<5|c] ^=a[(k -(b+1))
<<5|c]^= a[b<<5 |c]^=a[ (k-(b+1 ))<<5|c]
;printf( a[b<<5|c ]?"%-4d" :" " ,a[b<<5
|c]);} putchar( '\n');}} /*Mike Laman*/
也不去那里。因此它必须是可读的。
我举这些例子的重点是什么?并不是语言特性导致代码不可读、不可维护。这不是语法造成的。这是糟糕的程序员造成的。而糟糕的程序员,正如你在上面的项目中所看到的,可以使任何语言特性无法阅读和使用。比如上面的for循环。(你能看到他们,对吧?)
公平地说,有些语言结构比其他结构更容易被滥用。然而,如果你是一个C程序员,我会更仔细地观察#define大约50%的使用情况,然后才会开始反对goto!
因此,对于那些已经阅读到这里的人来说,有几个关键点需要注意。
Dijkstra's paper on goto statements was written for a programming environment where goto was a lot more potentially damaging than it is in most modern languages that aren't an assembler. Automatically throwing away all uses of goto because of this is about as rational as saying "I tried to have fun once but didn't like it so now I'm against it". There are legitimate uses of the modern (anaemic) goto statements in code that cannot be adequately replaced by other constructs. There are, of course, illegitimate uses of the same statements. There are, too, illegitimate uses of the modern control statements like the "godo" abomination where an always-false do loop is broken out of using break in place of a goto. These are often worse than judicious use of goto.
盲目地遵循最佳实践并不是最佳实践。避免将goto语句作为流控制的主要形式是为了避免产生难以阅读的意大利面条代码。如果在正确的地方谨慎使用,它们有时可以成为表达想法的最简单、最清晰的方式。Walter Bright, Zortech c++编译器和D编程语言的创造者,经常使用它们,但很明智。即使使用了goto语句,他的代码仍然完全可读。
底线:为了避免去那里而避免去那里是毫无意义的。您真正需要避免的是生成不可读的代码。如果充满goto的代码是可读的,那么它就没有任何问题。
当然,可以使用GOTO,但是有一件事比代码风格更重要,或者在使用它时,您必须考虑到代码是否可读:其中的代码可能不像您想象的那样健壮。
例如,看看下面的两个代码片段:
If A <> 0 Then A = 0 EndIf
Write("Value of A:" + A)
GOTO的等效代码
If A == 0 Then GOTO FINAL EndIf
A = 0
FINAL:
Write("Value of A:" + A)
我们首先想到的是这两段代码的结果将是“Value of A: 0”(当然,我们假设执行没有并行性)
这是不正确的:在第一个示例中,A将始终为0,但在第二个示例中(使用GOTO语句)A可能不是0。为什么?
原因是,从程序的另一点,我可以插入一个GOTO FINAL而不控制a的值。
这个例子非常明显,但是随着程序变得越来越复杂,看到这些东西的难度也增加了。
相关材料可以在Dijkstra先生的著名文章“反对GO TO声明的案例”中找到
在Perl中,使用标签从循环中“goto”—使用“last”语句,这类似于break。
这样可以更好地控制嵌套循环。
也支持传统的goto标签,但我不确定是否有太多的实例,这是实现您想要的结果的唯一方法-子例程和循环应该足以满足大多数情况。
使用“goto”使你的代码更易于阅读或运行得更快。只是不要让它把你的代码变成意大利面条。