【问题标题】:How to break out of a loop from inside a switch?如何从开关内部跳出循环?
【发布时间】:2010-11-28 01:09:16
【问题描述】:

我正在编写一些看起来像这样的代码:

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        break; // **HERE, I want to break out of the loop itself**
    }
}

有什么直接的方法吗?

我知道我可以使用一个标志,并通过在切换后放置一个条件中断来中断循环。我只是想知道 C++ 是否已经为此提供了一些构造。

【问题讨论】:

  • 为什么切换后需要条件中断?只需将您的 while 从 while(true) 更改为 while(flag)...
  • @Dave_Jarvis 我认为这是一个简化版本,他放在这里是为了说明他想要做什么。
  • 如果您是这些编写长达数页的函数的程序员之一,您会发现goto 很有吸引力,有时是唯一干净的出路。如果您倾向于将代码组织成只有几行长的小函数,并且每个函数只做一件事,那么您将永远不会遇到这个问题。 (顺便说一句,您的代码也会更容易阅读。)
  • 当您只想知道如何去最近的地铁站时,如果想要得到戒烟建议。
  • @hacker:好吧,如果你因为烟雾而看不到你面前的地铁站,那么这个建议可能不是那么糟糕。 :)

标签: c++ loops syntax switch-statement break


【解决方案1】:

您可以使用goto

while ( ... ) {
   switch( ... ) {
     case ...:
         goto exit_loop;

   }
}
exit_loop: ;

【讨论】:

  • 只是不要用那个关键字疯狂。
  • 很有趣,我被否决了,因为人们不喜欢goto。 OP 明确提到不使用标志。考虑到 OP 的限制,您能否提出更好的方法? :)
  • 只赞成 t 以补偿盲目的 goto 仇恨者。我猜 Mehrdad 知道他会因为在这里提出合理的解决方案而丢掉几分。
  • +1,恕我直言,没有更好的方法,即使考虑到 OPs 的限制。标志很丑陋,会破坏整个循环中的逻辑,因此更难掌握。
  • 归根结底,如果没有 goto 构造,所有语言都会失败。高级语言会混淆它,但如果你仔细研究引擎盖下的内容,你会发现每个循环或条件都归结为条件和无条件分支。我们通过使用 for、while、until、switch、if 关键字来欺骗自己,但最终它们都以一种或另一种方式使用 GOTO(或 JMP)。你可以欺骗自己,发明各种巧妙的方法来隐藏它,或者你可以务实地诚实,在适当的时候谨慎使用 goto。
【解决方案2】:

另一种解决方案是将关键字continuebreak 结合使用,即:

for (;;) {
    switch(msg->state) {
    case MSGTYPE:
        // code
        continue; // continue with loop
    case DONE:
        break;
    }
    break;
}

使用continue 语句完成您希望循环继续的每个案例标签,并使用break 语句完成应该终止循环的案例标签。

当然,这个解决方案只有在 switch 语句之后没有额外的代码执行时才有效。

【讨论】:

  • 虽然这确实非常优雅,但它的缺点是大多数开发人员首先必须查看它一分钟才能了解它如何/是否有效。 :(
  • 最后一句话是什么让这不是那么好:(否则一个非常干净的实现。
  • 仔细想想,没有计算机代码是好的。我们只是受过训练才能理解它。例如,在我学会之前,xml 看起来像垃圾。现在它看起来不那么垃圾了。 for(int x=0; x
【解决方案3】:

前提

无论语言或所需功能如何,都应将以下代码视为错误格式:

while( true ) {
}

支持论据

while( true ) 循环的形式很糟糕,因为它:

  • 打破了 while 循环的隐含契约。
    • while 循环声明应明确声明 only 退出条件。
  • 暗示它永远循环。
    • 必须阅读循环内的代码才能理解终止子句。
    • 永远重复的循环会阻止用户从程序中终止程序。
  • 效率低下。
    • 有多个循环终止条件,包括检查“真”。
  • 容易出现错误。
    • 无法轻松确定将始终在每次迭代中执行的代码的放置位置。
  • 导致不必要的复杂代码。
  • 自动源代码分析。
    • 要查找错误、程序复杂性分析、安全检查或在不执行代码的情况下自动导出任何其他源代码行为,指定初始中断条件允许算法确定有用的不变量,从而改进自动源代码分析指标。
  • 无限循环。
    • 如果每个人都总是将while(true) 用于不是无限的循环,那么当循环实际上没有终止条件时,我们就失去了简洁通信的能力。 (可以说,这已经发生了,所以这一点没有实际意义。)

“转到”的替代方法

下面的代码是更好的形式:

while( isValidState() ) {
  execute();
}

bool isValidState() {
  return msg->state != DONE;
}

优势

没有标志。没有goto。没有例外。容易改变。易于阅读。易于修复。另外代码:

  1. 将循环工作负载的知识与循环本身隔离开来。
  2. 允许维护代码的人轻松扩展功能。
  3. 允许在一处指定多个终止条件。
  4. 将终止子句与要执行的代码分开。
  5. 对核电站更安全。 ;-)

第二点很重要。在不知道代码是如何工作的情况下,如果有人让我让主循环让其他线程(或进程)拥有一些 CPU 时间,我会想到两种解决方案:

选项 #1

准备好插入暂停:

while( isValidState() ) {
  execute();
  sleep();
}

选项 #2

覆盖执行:

void execute() {
  super->execute();
  sleep();
}

此代码比嵌入switch 的循环更简单(因此更易于阅读)。 isValidState 方法应该只确定循环是否应该继续。该方法的主力应该被抽象为execute 方法,它允许子类覆盖默认行为(使用嵌入的switchgoto 是一项艰巨的任务)。

Python 示例

对比 StackOverflow 上发布的以下答案(针对 Python 问题):

  1. 永远循环。
  2. 要求用户输入他们的选择。
  3. 如果用户的输入是“重新启动”,则永远继续循环。
  4. 否则,永远停止循环。
  5. 结束。
代码
while True: 
    choice = raw_input('What do you want? ')

    if choice == 'restart':
        continue
    else:
        break

print 'Break!' 

对比:

  1. 初始化用户的选择。
  2. 循环,而用户的选择是单词“restart”。
  3. 要求用户输入他们的选择。
  4. 结束。
代码
choice = 'restart';

while choice == 'restart': 
    choice = raw_input('What do you want? ')

print 'Break!'

这里,while True 会导致误导和过于复杂的代码。

【讨论】:

  • while(true) 应该有害的想法对我来说似乎很奇怪。有些循环在执行前检查,有些在执行后检查,有些在中间检查。由于后者在 C 和 C++ 中没有语法结构,因此您必须使用 while(true)for(;;)。如果您认为这是错误的,那么您还没有对不同类型的循环给予足够的考虑。
  • 我不同意的原因:while(true) 和 for(;;;) 并不令人困惑(与 do{}while(true) 不同),因为它们直接说明了他们正在做的事情。查找“break”语句实际上比解析任意循环条件并查找它的设置位置更容易,而且证明正确性也不难。关于在哪里放置代码的争论在 continue 语句的存在下同样难以处理,而 if 语句也好不到哪里去。效率论点非常愚蠢。
  • 当你看到“while(true)”时,你可以想到一个有内部终止条件的循环,这并不比任意结束条件更难。简单地说,一个“while(foo)”循环,其中 foo 是一个以任意方式设置的布尔变量,并不比带有中断的“while(true)”好。在这两种情况下,您都必须查看代码。顺便说一句,提出一个愚蠢的理由(微效率是一个愚蠢的论点)对您的案子没有帮助。
  • @Dave:我给出了一个理由:这是获得一个检查中间条件的循环的唯一方法。经典示例:while(true) {string line; std::getline(is,line); if(!is) break; lines.push_back(line);} 当然,我可以将其转换为预条件循环(使用std::getline 作为循环条件),但这本身就有缺点(line 必须是循环范围之外的变量)。如果我这样做了,我将不得不问:为什么我们仍然有三个循环?我们总是可以将所有东西都转换成一个预处理循环。使用最合适的。如果while(true) 适合,请使用它。
  • 作为对阅读此答案的人们的礼貌,我会避免评论产生问题的商品的质量,并坚持回答问题本身。问题是以下问题的变体,我相信这是许多语言的一个特征:你有一个嵌套在另一个循环中的循环。您想从内循环中跳出外循环。这是怎么做到的?
【解决方案4】:

一种巧妙的方法是将其放入一个函数中:

int yourfunc() {

    while(true) {

        switch(msg->state) {
        case MSGTYPE: // ... 
            break;
        // ... more stuff ...
        case DONE:
            return; 
        }

    }
}

可选(但“不好的做法”):如前所述,您可以使用 goto,或在 switch 内抛出异常。

【讨论】:

  • Exception 应该用于抛出异常,而不是众所周知的函数行为。
  • 我同意来世:把它放在一个函数中。
  • 在这种情况下抛出异常真的很邪恶。它的意思是“我想使用 goto,但我在某处读到不应该使用它们,所以我会用诡计假装看起来很聪明”。
  • 我同意抛出异常或使用 goto 是一个糟糕的主意,但它们是可行的选项,并且已在我的回答中列出。
  • 抛出异常就是用 COMEFROM 代替 GOTO。不要这样做。 goto 效果更好。
【解决方案5】:

AFAIK 在 C++ 中没有“双重中断”或类似结构。最接近的是goto——虽然它的名字有不好的含义,但它存在于语言中是有原因的——只要小心谨慎地使用它,它就是一个可行的选择。

【讨论】:

    【解决方案6】:

    你可以像这样把你的开关放到一个单独的函数中:

    bool myswitchfunction()
    {
        switch(msg->state) {
        case MSGTYPE: // ... 
            break;
        // ... more stuff ...
        case DONE:
            return false; // **HERE, I want to break out of the loop itself**
        }
        return true;
    }
    
    while(myswitchfunction())
        ;
    

    【讨论】:

      【解决方案7】:

      即使你不喜欢 goto,也不要使用异常来退出循环。以下示例显示了它的丑陋程度:

      try {
        while ( ... ) {
          switch( ... ) {
            case ...:
              throw 777; // I'm afraid of goto
           }
        }
      }
      catch ( int )
      {
      }
      

      我会使用goto,就像this 答案一样。在这种情况下,goto 将使代码比任何其他选项更清晰。我希望this 的问题会有所帮助。

      但我认为使用goto 是这里唯一的选择,因为字符串while(true)。您应该考虑重构循环。我想以下解决方案:

      bool end_loop = false;
      while ( !end_loop ) {
          switch( msg->state ) {
          case MSGTYPE: // ... 
              break;
          // ... more stuff ...
          case DONE:
              end_loop = true; break;
          }
      }
      

      甚至如下:

      while ( msg->state != DONE ) {
          switch( msg->state ) {
          case MSGTYPE: // ... 
              break;
          // ... more stuff ...
      }
      

      【讨论】:

      • 是的,但是 更不喜欢例外 ;-)
      • 使用异常来模拟 goto 当然比实际使用 goto 更糟糕!它也可能会明显变慢。
      • @Downvoter:是因为我声明,你不应该使用异常,还是因为我建议重构?
      • 不是反对者-但是...您的答案尚不清楚您是提议还是谴责使用异常退出循环的想法。也许第一句话应该是:“即使你不喜欢goto,也不要使用异常退出循环:”?
      • @Jonathan Leffler,谢谢,您展示了有价值的评论样本。更新了答案,记住你的 cmets。
      【解决方案8】:

      在这种情况下,没有用于跳出循环的 C++ 构造。

      要么使用标志来中断循环,要么(如果合适)将代码提取到函数中并使用return

      【讨论】:

        【解决方案9】:

        不,C++ 没有这种结构,因为关键字“break”已经保留用于退出 switch 块。或者,带有退出标志的 do..while() 就足够了。

        do { 
            switch(option){
                case 1: ..; break;
                ...
                case n: .. ;break;
                default: flag = false; break;
            }
        } while(flag);
        

        【讨论】:

          【解决方案10】:

          您可能会使用 goto,但我更愿意设置一个停止循环的标志。然后跳出开关。

          【讨论】:

            【解决方案11】:

            为什么不直接修复 while 循环中的条件,让问题消失?

            while(msg->state != DONE)
            {
                switch(msg->state) {
                case MSGTYPE: // ... 
                    break;
                // ... more stuff ...
                case DONE:
                    // We can't get here, but for completeness we list it.
                    break; // **HERE, I want to break out of the loop itself**
                }
            }
            

            【讨论】:

              【解决方案12】:

              考虑到解释的深度,这让我感到惊讶......这就是你所需要的......

              bool imLoopin = true;
              
              while(imLoopin) {
              
                  switch(msg->state) {
              
                      case MSGTYPE: // ... 
                          break;
              
                      // ... more stuff ...
              
                      case DONE:
                          imLoopin = false;
                          break;
              
                  }
              
              }
              

              哈哈!!真的!这就是你所需要的!一个额外的变量!

              【讨论】:

                【解决方案13】:

                我认为;

                while(msg->state != mExit) 
                {
                    switch(msg->state) 
                    {
                      case MSGTYPE: // ...
                         break;
                      case DONE:
                      //  .. 
                      //  ..
                      msg->state =mExit;
                      break;
                    }
                }
                if (msg->state ==mExit)
                     msg->state =DONE;
                

                【讨论】:

                  【解决方案14】:

                  我遇到了同样的问题并使用标志解决了。

                  bool flag = false;
                  while(true) {
                      switch(msg->state) {
                      case MSGTYPE: // ... 
                          break;
                      // ... more stuff ...
                      case DONE:
                          flag = true; // **HERE, I want to break out of the loop itself**
                      }
                      if(flag) break;
                  }
                  

                  【讨论】:

                  • 整洁!我正要写同样的答案。
                  【解决方案15】:

                  因为switch使用breakswitch(而不是while(1))突围,所以需要goto-语句:

                  while(1) {
                      switch (*p) {
                        case ' ':
                          p++;
                          break;
                        case '\n':
                          p++; *s=p; return(i);
                        case '\0':
                          *s=p; return(i);
                        default:
                          token[i]=p;
                          i++;
                          p++;
                          goto ex1;
                      };
                    };
                    ex1:
                  

                  我不能将多个案例添加到同一行,例如:

                  case '','\t':

                  应该是

                  案例'':案例'\t':

                  这就是为什么这里使用的 break...

                  看起来最常见的情况应该放在列表的顶部,以使程序运行得更快。 它可能没有并行执行来搜索不同的案例。

                  标准 c 可能缺少一些关于此切换的方法: https://blog.hackajob.co/better-c-switch-statements-for-a-range-of-values/ => 允许您在 switch 表达式中使用 、= 运算符

                  我在想它应该是(如果 c 语言语法改变的话):

                  switch (c) {
                    case >= 5:
                       ... op1
                    endcase;
                    case == 1:
                    case == 3:
                       ... op2
                    endcase;
                    default:
                      ...
                  };
                  

                  其中 op2 在 c 等于 1 或 3 时执行,当 c 大于或等于 5 时执行 op1。 因为以类似的方式很容易进行相等或大于/小于的比较。

                    while(1) {
                      switch (c) {
                        case >= 2:
                           ... op1
                        case <= 5:
                           ... op2
                        break;
                        default:
                          ...
                      };
                    };
                  

                  这种情况 op1 对大于 2 的 c 执行,op2 对 2

                  【讨论】:

                    【解决方案16】:

                    最简单的方法是在执行 SWITCH 之前放置一个简单的 IF ,然后 IF 测试您退出循环的条件 .... 尽可能简单

                    【讨论】:

                    • 嗯...通过将条件放在 while 参数中,您可以更简单。我的意思是这就是它的目的! :)
                    【解决方案17】:

                    C++ 中的break 关键字仅终止最嵌套的封闭迭代或switch 语句。因此,您不能直接在switch 语句中跳出while (true) 循环;但是您可以使用以下代码,我认为这是解决此类问题的绝佳模式:

                    for (; msg->state != DONE; msg = next_message()) {
                        switch (msg->state) {
                        case MSGTYPE:
                            //...
                            break;
                    
                        //...
                        }
                    }
                    

                    如果您需要在msg-&gt;state 等于DONE 时执行某些操作(例如运行清理例程),则将该代码立即放在for 循环之后;即,如果您目前有:

                    while (true) {
                        switch (msg->state) {
                        case MSGTYPE:
                            //... 
                            break;
                    
                        //...
                    
                        case DONE:
                            do_cleanup();
                            break;
                        }
                    
                        if (msg->state == DONE)
                            break;
                    
                        msg = next_message();
                    }
                    

                    然后改用:

                    for (; msg->state != DONE; msg = next_message()) {
                        switch (msg->state) {
                        case MSGTYPE:
                            //...
                            break;
                    
                        //...
                        }
                    }
                    
                    assert(msg->state == DONE);
                    do_cleanup();
                    

                    【讨论】:

                      【解决方案18】:
                      while(MyCondition) {
                      switch(msg->state) {
                      case MSGTYPE: // ... 
                          break;
                      // ... more stuff ...
                      case DONE:
                         MyCondition=false; // just add this code and you will be out of loop.
                          break; // **HERE, you want to break out of the loop itself**
                      }
                      }
                      

                      【讨论】:

                        【解决方案19】:

                        如果我记得 C++ 语法,你可以给break 语句添加一个标签,就像goto 一样。所以你想要的很容易写出来:

                        while(true) {
                            switch(msg->state) {
                            case MSGTYPE: // ...
                                break;
                            // ... more stuff ...
                            case DONE:
                                break outofloop; // **HERE, I want to break out of the loop itself**
                            }
                        }
                        
                        outofloop:
                        // rest of your code here
                        

                        【讨论】:

                        • 很遗憾,你的内存有问题。这将是一个很好的功能,在极少数情况下它会很有用。 (我已经看到了要突破的关卡数量的建议,但在我看来,这些建议就像是等待发生的错误。)
                        • 那一定是Java。谢谢回答。而且,当然,其他人也是对的。
                        【解决方案20】:
                          while(true)
                          {
                            switch(x)
                            {
                             case 1:
                             {
                              break;
                             }
                            break;
                           case 2:
                            //some code here
                           break;
                          default:
                          //some code here
                          }
                        }
                        

                        【讨论】:

                        • 该代码示例中的所有中断都超出了 switch 语句。
                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2013-12-12
                        • 1970-01-01
                        • 2012-01-16
                        • 1970-01-01
                        • 2011-10-11
                        • 1970-01-01
                        • 2020-08-08
                        相关资源
                        最近更新 更多