除非使用System.out.println,否则看似无限循环终止

Oma*_*mar 88 java for-loop infinite-loop

我有一个简单的代码,应该是一个无限循环,因为x它将一直在增长,并将永远保持大于j.

int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
   x = x + y;
}
System.out.println(y);
Run Code Online (Sandbox Code Playgroud)

但就像它一样,它打印y并且不会无休止地循环.我无法弄清楚为什么.但是,当我按以下方式调整代码时:

int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
    x = x + y;
    System.out.println(y);
}
System.out.println(y);
Run Code Online (Sandbox Code Playgroud)

它变成了无限循环,我不明白为什么.java是否认识到它是无限循环并在第一种情况下跳过它但是必须在第二种情况下执行方法调用,以便它按预期运行?困惑:)

Zby*_*000 160

这两个例子都不是无穷无尽的.

问题是intJava中的类型限制(或几乎任何其他常用语言).当x达到的值时0x7fffffff,添加任何正值将导致溢出并x变为负值,因此低于j.

第一个和第二个循环之间的区别在于内部代码需要花费更多时间,并且可能需要几分钟才能x溢出.对于第一个示例,它可能需要不到第二个或最有可能的代码将被优化器删除,因为它没有任何影响.

正如讨论中所提到的,时间将在很大程度上取决于OS如何缓冲输出,是否输出到终端仿真器等,因此它可能远高于几分钟.

  • 我刚尝试了一个程序(在我的笔记本电脑上),它在一个循环中打印一行.我计时了,它能够打印大约1000行/秒.根据N00b的评论,循环将执行238609294次,循环终止将花费大约23861秒 - 超过6.6小时.比"几分钟"多一点. (47认同)
  • @ajb:取决于实施.Windows上的IIRC`println()`是一个阻塞操作,而在(某些?)Unix上,它是缓冲的,所以速度要快得多.还可以尝试使用`print()`,它会缓冲直到遇到`\n`(或缓冲区填充,或`flush()`被调用) (11认同)
  • 它还取决于显示输出的终端.有关一个极端的例子,请参见http://stackoverflow.com/a/21947627/53897(减速是由于自动换行) (6认同)
  • @Zbynek哦,可能是这样,但这让我想起,终端I/O通常会被线路缓冲,而不是阻塞,因此很可能每个println都会导致系统调用,从而进一步降低终端机箱速度. (2认同)

小智 33

由于它们被声明为int,一旦达到最大值,循环就会中断,因为x值将变为负值.

但是当System.out.println添加到循环中时,执行速度变得可见(因为输出到控制台会降低执行速度).但是,如果让第二个程序(循环中具有syso的程序)运行的时间足够长,它应该具有与第一个程序相同的行为(循环中没有syso的程序).

  • 人们没有意识到控制台发送垃圾邮件会减慢代码的速度. (21认同)

小智 13

这可能有两个原因:

  1. Java优化了for循环,因为x在循环之后没有使用,只需删除循环.您可以通过System.out.println(x);在循环后放置语句来检查这一点.

  2. 有可能Java实际上并没有优化循环,它正在正确执行程序,最终x会变得太大int而且溢出.整数溢出很可能使整数x为负值,它将小于j,因此它将从循环中出来并打印出值y.这也可以通过System.out.println(x);在循环后添加来检查.

此外,即使在第一种情况下,最终会发生溢出,从而将其呈现为第二种情况,因此它永远不会是真正的无限循环.

  • 我选择门号2. (14认同)
  • 1.会是一个错误.不允许编译器优化更改程序的行为.如果这是一个无限循环,那么编译器可以优化所需的全部,但是,结果必须仍然是无限循环.真正的解决方案是OP错误:两者都不是无限循环,一个只是做比另一个更多的工作,所以它需要更长的时间. (4认同)