继续语句如何工作?

28

我正在尝试理解“continue”的工作原理。我理解了这个关键字的概念,但是当我运行不同的程序时,它的表现却不同:-/ 以下是几个例子:

如果我运行这个程序:

int j = 0;
int i = 0;
LABEL1: for (; i < 3; i++) {
  if (true)
    continue;
}

i的值将变为3。目前为止一切顺利。现在我们添加一个外部循环:

int j = 0;
int i = 0;
LABEL2: for (; j < 3; j++) {
  LABEL1: for (; i < 3; i++) {
    if (true)
      continue LABEL2;
  }
}

i的值将会是...0! 如果在带有指向外部循环的标签的continue语句中使用,我不明白为什么i没有增加。有人可以解释一下吗? 类似于break或do{} while这样的东西有什么棘手的问题吗?

非常感谢您能提供的任何帮助。


12
i 没有被增加,因为你继续了外层循环。 - Jay Harris
1
使用标签继续,将会跳转到标签指示的位置。在这里,continue 跳转到 LABEL2,因此 i++ 不会被执行。 - user3761116
谢谢大家!我明白了。你们帮了我很多! 我刚刚发现了一些可能有助于其他人理解如何在循环中使用continue的内容: continue会导致控制流跳转到循环条件(对于while、do while循环)或更新(对于for循环)。这将导致控制流跳转到下一个迭代。 嘿,能把它标记为已解决吗? - user3884432
@user3884432,为了“标记为已解决”,请从以下答案中选择您最喜欢的答案。有很多好的答案可供选择。如果您不喜欢它们,请编写自己的答案并接受它。 - Boris the Spider
实际上,continue 会将你分支到循环的结尾,就在闭合的 } 之前。这导致循环条件被重新评估,就像程序流程“落入”循环底部一样。在 continue label; 的情况下,控制转移到由 label: 标识的循环底部。 - Hot Licks
9个回答

28
基本的 for 语句结构如下:
BasicForStatement:
for ( [ForInit] ; [Expression] ; [ForUpdate] ) Statement
现在,引用自 JLS §14.14.1.3. Abrupt Completion of for Statement
如果由于没有标签的continue而使语句的执行突然终止,则按顺序执行以下两个步骤:
  1. 首先,如果存在ForUpdate部分,则按从左到右的顺序评估表达式;它们的值(如果有)将被丢弃。 如果不存在ForUpdate部分,则不执行任何操作。

  2. 其次,执行另一个for迭代步骤。

如果由于带有标签Lcontinue而使语句的执行突然终止,则有一个选择:
  • 如果for语句具有标签L,则按顺序执行以下两个步骤:

    1. 首先,如果存在ForUpdate部分,则按从左到右的顺序评估表达式;它们的值(如果有)将被丢弃。 如果不存在ForUpdate部分,则不执行任何操作。

    2. 其次,执行另一个for迭代步骤。

  • 如果for语句没有标签L,则由于带有标签Lcontinue而使for语句突然终止。

(我加粗了重点)

对于你的情况,i++ 就是 ForUpdate。根据上面所述:

  • 你的第一个代码片段符合第一种情况,因此会增加 i 的值。

  • 你的第二个代码片段符合第二种情况,因此 i 的值不会增加,因为该 for 语句会突然终止。

请注意,如果你的第二个代码片段中有 continue LABEL1,那么 i 的值会像在第一个代码片段中一样增加(这符合 JLS 规定)。

作为未来的提示,关于语言规则/语义的 确定性答案,你应该始终查阅语言规范。对于 Java,这就是 JLS


还有一点需要注意,14.14.1.2规定[Expression]的评估是迭代步骤的一部分。否则,对规范的天真阅读可能会表明两个代码片段都不应该终止。 - Taemyr

14

这很正常。执行流程如下:

  1. j=0
  2. i=0
  3. 进入标签2的循环,
  4. 进入标签1的循环,
  5. 继续回到标签2
  6. j++
  7. 进入标签1的循环,
  8. 继续回到标签2
  9. j++
  10. 进入标签1的循环,
  11. 继续回到标签2 ... 直到j==3

2
在第一个片段中展示不同之处会有所帮助。 - arshajii
6
这不值得点赞。它没有解释“continue”的作用。展示执行流程并不是一个答案。 - Dioxin
1
@VinceEmigh 这很有用,但在评论中格式化太长了。如果您认为它不适合作为答案,可能会成为一个好的元主题。 - Reinstate Monica Please
为了得到一份顶级的答案,流程图会极大地提高答案的可读性。现在的答案有些拥挤。 - randomusername
实际上,我想要展示的是这里的问题不在于continue如何工作,而在于for循环如何工作。 - Frank

7

内部循环结束时会增加 i,但是使用 continue LABEL2 跳出内部循环,到达外部循环结尾,因此 i 没有增加;只有在满足外部循环条件之前,j 才会递增。

也许当我们用 while 循环重新编写代码时,这一点就变得更清晰了:

int j=0;
int i = 0;
while (j<3) {
    while(i<3) {
       if (true)
           goto END_OF_LOOP2;

       END_OF_LOOP1: i++;
    }
    END_OF_LOOP2: j++;
}

6

Continue的意思就是它的名字所暗示的,它会立即跳过当前循环中代码的剩余部分,继续执行下一个循环。

如果你有如下的循环:

int j=0;
int i = 0;
    LABEL1 : for(;i < 3; i++){
        if (true) {
            continue;
        }
        someOtherMethod();
    }

someOtherMethod部分永远不会被执行,因为你总是会触发continue语句。

你的计数器从未增加的原因与标签有关,可以使用标签(在你的情况下为LABEL1LABEL2)标记循环,并使用该标签来继续内部循环的外部循环之一。

因此,在你的代码中,LABEL1循环从未有机会增加计数器,因此i保持为0,因为continue语句立即继续到外部循环的下一次迭代。


4
continue LABEL2; 

只有外层循环增加的原因。如果您有
LABEL2 : for(;j <3;j++){
    LABEL1 : for(;i < 3; i++){
             if (true)
                 continue LABEL1;
             }
     }
将会增加。

3
如果这是代码,例如:

如果这是代码,例如:

    int main (){
      // Local variable declaration:

    int a = 10;

   // do loop execution
  do
  {
     if( a == 15)
     {
        // skip the iteration.
        a = a + 1;
        continue;
      }
      cout << "value of a: " << a << endl;
           a = a + 1;
         }while( a < 20 );
         return 0;
     }

那么结果将是:
value of a: 10
value of a: 11
value of a: 12
value of a: 13
value of a: 14
value of a: 16
value of a: 17
value of a: 18
value of a: 19

需要注意的一点是:"a的值为15"不在输出中。 编程语言中的continue指令会跳过循环中特定的行,如果使用break指令,则会在"14"这行后退出循环(在本例中)。 为了方便理解,请参考以下流程图:

enter image description here


3

下一次检查条件时会增加 i 的值。

如果使用 continuebreak 跳出循环,则不会再次检查条件,i 的值也不会再增加。

for

LABEL1 : for(;i < 3; i++){
               if (true)
                   continue;
             }

当你继续执行时,你会再次检查此时i的值是否小于3,在检查该条件之前,编译器会在内部递增i的值。

这里:

LABEL2 : for(;j <3;j++){
        LABEL1 : for(;i < 3; i++){
                 if (true)
                     continue LABEL2;
                 }
         }

当您执行continue命令时,必须再次检查Label1条件,否则将直接跳转到具有Label2的语句,因此它永远不会增加。

对于break也是一样。

LABEL1 : for(;i < 3; i++){
               if (true)
                   break;
             }

如果你执行上述代码,i的值将是0而不是1


2
如果您在调试模式下运行此代码,您将看到发生了什么。简而言之,每当您进入内部循环以增加i时,您将跳过增加i并转而跳转到LABEL2,而不是执行i的增量操作并继续内部循环的下一次迭代。
因此,您将在外部循环中循环3次,并且永远不会触发内部循环中的i++操作。
如果更容易理解,请将内部for循环视为while循环:
int i = 0, j = 0;
LABEL2: for (; j < 3; j++) {
  LABEL1: while (i < 3) {
    if (true) {
      continue LABEL2;
    }

    i++;
  }
}

0
没有标签的continue语句将从最内层的while或者do循环条件处,或者从最内层的for循环更新表达式处重新执行。它经常被用来提前终止循环处理,从而避免深度嵌套的if语句。在下面的示例中,continue将获得下一行,而不会处理循环中的以下语句。
while (getNext(line)) {
  if (line.isEmpty() || line.isComment())
    continue;
  // More code here
}

使用标签continue将以相同的方式执行相应标记的循环。这可用于逃脱深度嵌套的循环,或仅为了清晰明了。如果您真的很恶劣,还可以使用它来模拟有限形式的goto。在以下示例中,continue将重新执行for (;;)循环。

aLoopName: for (;;) {
  // ...
  while (someCondition)
  // ...
    if (otherCondition)
      continue aLoopName;

有时候,continue 也被用作占位符,以便使空循环体更清晰明了。
for (count = 0; foo.moreData(); count++)   continue;

在C和C++中也存在没有的相同语句。在Perl中,它被命名为next

source


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接