while(true);当不是't在一片空白中
while(true); loop throws Unreachable code when isn't in a void
我用java做了一些小程序。我知道如果我写while(true);
,程序将在这个循环中冻结。如果代码是这样的:
测试1:
public class While {
public static void main(String[] args) {
System.out.println("start");
while (true);
System.out.println("end");
}
}
编译器向我抛出错误:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unreachable code
at While.main(While.java:6)
我不知道这个错误存在。但我明白为什么要扔。当然,第6行无法访问,导致编译问题。然后我测试了这个:
测试2:
public class While {
public static void main(String[] args) {
System.out.println("start");
a();
b();
}
static void a() {
while(true);
}
static void b() {
System.out.println("end");
}
}
由于某种原因程序正常运行(控制台打印"启动",然后冻结(。编译器无法检查void a()
的内部并确定它不可访问。可以肯定的是,我试过了:
测试3:
public class While {
public static void main(String[] args) {
System.out.println("start");
a();
System.out.println("end");
}
static void a() {
while(true);
}
}
与测试2的结果相同。
经过一些研究,我发现了这个问题因此,如果括号内的代码是一个变量,编译器就不会抛出异常。这是有道理的,但我不认为这同样适用于voids
。
Q:那么,如果无法访问void b()
(测试2(和System.out.println("end");
?
编辑:我尝试了C++中的测试1:
#include <iostream>
using namespace std;
int main()
{
cout << "start" << endl;
while(true);
cout << "end" << endl;
return 0;
}
编译器没有抛出任何错误,然后我得到了与测试2和测试3相同的结果。所以我想这是java的东西?
语言规范有一个确切的定义,编译器应该将其视为不可访问的代码,另请参阅https://stackoverflow.com/a/20922409/14955.
特别是,它不关心一个方法是否完成,也不查看其他方法内部。
它不会做更多的事情。
然而,你可以使用像FindBugs这样的静态代码分析工具来进行"更深层次"的分析(不过,也不确定它们是否检测到了你描述的模式,而且,正如其他人所指出的那样,无论如何,在所有通用性中的停顿问题都无法通过算法解决,因此必须对"尽最大努力"的一些合理定义划清界限(。
通常,不可能绝对确定地确定某个东西是否可到达。
为什么?这相当于暂停问题。
停顿的问题是:
给定任意计算机程序的描述,决定该程序是结束运行还是永远继续运行。
这个问题已被证明是无法解决的。
X段代码是否可访问,与之前的代码是否会停止是一样的。
因为这是一个无法解决的问题,编译器(在Java或任何其他语言中(都不会很努力地解决它。如果它碰巧确定它真的无法访问,那么你就会收到警告。如果没有,它可能是可访问的,也可能是不可访问的。
在Java中,无法访问的代码是编译器错误。因此,为了保持兼容性,语言规范准确地定义了编译器应该努力的程度。(根据其他答案,这是"不要进入另一个功能"。(
在其他语言(如C++(中,编译器可能会进一步进行优化。(内联函数并发现它永远不会返回后,可能会检测到无法访问的代码。(
不可访问代码是一个编译时错误,它只是说这个程序的流程没有意义;有些东西永远不会到达'。
显然,由于无休止的循环,您的测试的执行方式是这样的,但为什么第一个测试会因编译时错误而失败呢?
while语句可以正常完成,如果以下是真的:
while语句是可访问的,而条件表达式不是值为true的常量表达式(§15.28(。
有一个可到达的break语句退出while语句。
好吧,但是方法调用(比如a()
(呢?为什么测试2和3能成功编译?
- 如果表达式语句是可访问的,则它可以正常完成
由于方法调用被视为一个表达式,因此只要在它阻塞其逻辑执行路径之前没有任何东西,它们将始终是可访问的。
为了更好地说明这种编译机制背后的一些推理,让我们以if
语句为例。
if(false)
System.out.println("Hello!"); // Never executes
以上内容在编译时是正确的(尽管许多IDE肯定会抱怨!(。
Java 1.7规范谈到了这一点:
这种不同处理的基本原理是允许程序员定义"标志变量",例如:
static final boolean DEBUG = false;
然后编写代码,例如:
if (DEBUG) { x=3; }
这个想法是应该有可能改变DEBUG的值从false变为true或从true变为false,然后在不对程序文本进行其他更改的情况下正确编译代码。
此外,实际上还有一个向后兼容性的原因:
这种"有条件编译"的能力对,二元兼容性及其关系(§13(。如果一组类使用这样一个"flag"变量的被编译,条件代码是省略,以后只分发的新版本是不够的包含标志定义的类或接口。A.因此,对标志值的更改是不兼容二进制的具有预先存在的二进制文件(§13.4.9(这种不兼容性,例如在case中使用常量switch语句中的标签;参见§13.4.9.(
Java编译器的大多数(根据规范((如果不是全部的话(实现都会而不是遍历到方法中。当解析Java代码本身时,它将a()
视为MethodInvocationElement
,这意味着该代码调用其他代码。我真的不在乎,我只是看语法在语法上,在调用a()
之后,后续代码的归属是有意义的。
请记住性能成本。编译已经花费了相当长的时间。为了保持快速,Java编译器实际上并没有递归到方法中;这需要很长时间(理论上,编译器必须评估很多很多代码路径(。
为了进一步重申它在语法上是驱动的,需要在a()
中的循环之后直接添加一个return;
语句。不编译,是吗但是,从语法上来说,没有它是有意义的。
答案在于Java语言规范为可达性制定的规则。它首先声明
如果一个语句由于不可访问而无法执行,则是编译时错误。
然后
while
语句可以正常完成,如果以下是真的:
while
语句是可达的,并且条件表达式不是值为true
的常量表达式(§15.28(- 有一个可访问的
break
语句退出while语句
和
表达式语句可以正常完成,前提是它是可访问的。
在第一个例子中,有一个while循环无法正常完成,因为它有一个条件,该条件是一个值为true
的常量表达式,并且其中没有可到达的break
。
在第二个和第三个示例中,表达式语句(方法调用(是可访问的,因此可以正常完成。
所以我想这是java的东西?
上面的规则是Java的规则。C++可能有自己的规则,其他语言也是如此。
- 使用strcpy将char数组的元素复制到另一个数组
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- 无法通过空白将文本文件行分隔为矢量
- 有一个打印语句的函数是一种糟糕的编程实践吗
- 在 c++ 中拥有一组结构的正确方法是什么?
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何将三维尺寸不固定的三维阵列展平为一维阵列
- nlohmann-json将一个数组插入到另一个数组中
- 有没有一种方法可以在编译时获得作用域类名
- 为什么make_tie不是一件事
- 对于C++中使用智能指针的指针算术限制,有没有一种变通方法
- c++中O(n^(1/3))中一个数的除数的有效计数
- 一种在C++中读取TXT配置文件的简单方法
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存
- 在调用接收数组的方法时,模板化数组大小是不是一种糟糕的做法
- c++是一种与空白无关的语言,这是规则的例外
- C++ 二叉树:返回节点的后代数.一片叶子有零.如果未找到树数据,则返回 -1
- C++生成随机数而不重复.输出屏幕只是一片空白,光标在闪烁
- while(true);当不是't在一片空白中