Day13-42.The infamous “goto”
来源:互联网 发布:属性数据分析引论 编辑:程序博客网 时间:2024/06/07 17:16
臭名昭著的goto
编程语言中一开始就有goto关键词。事实上,goto起源于汇编语言的程序控制:“若条件A成立,则跳到这里;否则跳到那里”。
如果阅读由编译器最终生成的汇编代码,就会发现程序控制里包含了许多跳转。
(Java编译器生成它自己的“汇编代码”,但是这个代码是运行在Java虚拟机上的,而不是直接运行在CPU硬件上。)
goto语句是在源码级上的跳转,这使其招致了不好的声誉。
若一个程序总是从一个地方跳到另一个地方,还有什么办法能识别程序的控制流程呢?
自从Edsger Dijkstra发表了著名论文《Goto considered harmful》 (Goto有害),众人开始痛斥goto的不是,甚至建议将它从关键词集合中扫地出门。
对于这个问题,中庸之道是最好解决方法。真正的问题并不在于使用goto,而在于goto的滥用;
而且少数情况下,goto还是组织控制流程的最佳手段。
尽管goto仍是Java中的一个保留字,但在语言中并未使用它;Java 没有goto。
然而,Java也能完成一些类似于跳转的操作,这与break和continue这两个关键词有关。
它们其实不是一个跳转,而是中断迭代语句的一种方法。
之所以把它们纳入goto问题中一起讨论,是由于它们使用了相同的机制:标签。
标签是后面跟有冒号的标识符,就像下面这样:
label1:
在Java中,标签起作用的唯一的地方刚好是在迭代语句之前。
”刚好之前“的意思表明,在标签和迭代之间置入任何语句都不好。
而在迭代之前设置标签的唯一理由是:我们希望在其中嵌套另一个迭代或者一个开关。
这是由于break和continue关键词通常只中断当前循环,但若随同标签一起使用,它们就会中断循环,之道标签所在的地方:
label1:outer-iteration{inner-iteration{//…break;//(1)//…continue;//(2)//…continue label1;//(3)//…break label1;//(4)}}
在(1)中,break中断内部迭代,回到外部迭代。
在(2)中,continue使执行点移回内部迭代的起始处。
(3)中,continue lable1同时中断内部迭代以及外部迭代,直接转到label1处;
随后,它实际上是继续迭代过程,但却从外部迭代开始。
在(4)中,break label1也会中断所有迭代,并回到labek1,但并不重新进入迭代,也就是说,它实际是完全终止了两个迭代。
下面是标签用于for循环的例子:
//: control/LabeledFor.java// For loops with "labeled break" and "labeled continue."import static net.mindview.util.Print.*;public class LabeledFor { public static void main(String[] args) { int i = 0; outer: // Can't have statements here for(; true ;) { // infinite loop inner: // Can't have statements here for(; i < 10; i++) { print("i = " + i); if(i == 2) { print("continue"); continue; } if(i == 3) { print("break"); i++; // Otherwise i never // gets incremented. break; } if(i == 7) { print("continue outer"); i++; // Otherwise i never // gets incremented. continue outer; } if(i == 8) { print("break outer"); break outer; } for(int k = 0; k < 5; k++) { if(k == 3) { print("continue inner"); continue inner; } } } } // Can't break or continue to labels here }} /* Output:i = 0continue inneri = 1continue inneri = 2continuei = 3breaki = 4continue inneri = 5continue inneri = 6continue inneri = 7continue outeri = 8break outer*///:~
注意,break会中断for循环,而且在抵达for循环的末尾之前,递增表达式不会执行。
由于break跳过了递增表达h式,所以在i==3的情况下直接对i执行递增运算。在i=7的情况下,continue outer语句会跳到循环顶部,而且也会跳过递增,所以这里也对i直接递增。
如果没有break outer语句,就没有办法从内部循环里跳出外部循环。
这是由于break本身只能中断最内层的循环(continue同样也是如此)。
当然,如果想在中断循环的同时退出,简单的用一个return即可。
下面这个例子向大家展示了带标签的break以及continue语句在while循环中的用法:
//: control/LabeledWhile.java// While loops with "labeled break" and "labeled continue."import static net.mindview.util.Print.*;public class LabeledWhile { public static void main(String[] args) { int i = 0; outer: while(true) { print("Outer while loop"); while(true) { i++; print("i = " + i); if(i == 1) { print("continue"); continue; } if(i == 3) { print("continue outer"); continue outer; } if(i == 5) { print("break"); break; } if(i == 7) { print("break outer"); break outer; } } } }} /* Output:Outer while loopi = 1continuei = 2i = 3continue outerOuter while loopi = 4i = 5breakOuter while loopi = 6i = 7break outer*///:~
同样的规则亦适用于while:
1.一般的continue会退回最内层循环的开头(顶部),并继续执行
2带标签的continue会到达标签的位置,并重新进入进阶在那个标签后面的循环。
3.一般的break会中断并跳出当前循环
4.带标签的break会中断并跳出标签所指的循环。
要记住的重点是:在Java里需要使用标签的唯一理由就是因为有循环嵌套存在,而且想从多层嵌套中break或continue。
在Dijkstra的《Goto有害》的论文中,他最反对的就是标签,而非goto。
他发现在一个程序里随着标签的增多,产生的错误也会越来越多,并且标签和goto使得程序难以分析。
但是,Java的标签不会造成这种问题,因为他们的应用场合已经受到了限制,没有特别的方式用于改变程序的控制。
由此也引出了一个有趣的现象:通过限制语句的能力,反而能使一项语言特性更加有用。
- Day13-42.The infamous “goto”
- The infamous sun.misc.Unsafe explained
- day13
- DAY13
- day13
- day13
- day13
- day13
- day13
- day13
- day13
- Day13
- Day13
- day13
- day13
- DAY13
- Day13
- GOTO
- vb调用脚本执行文件合并
- django(四)---数据库与ORM
- 单片机上内存管理(重定义malloc & free)de实现
- 正点原子提供免费开源的的连接机智云平台开发教程和实例源码
- opencv人脸检测
- Day13-42.The infamous “goto”
- 网络请求content-type
- 第一个Qt&C++开发项目所遇到的坑
- C中对于指针的理解
- select默认下拉箭头改变、option样式清除
- 数据结构希尔排序
- cocos2d-js 生命周期函数
- Altium Designer.v17.0.6 Build 354
- 1.7register寄存器