138

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

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

有没有直接的方法可以做到这一点?

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

4

20 回答 20

197

您可以使用goto.

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

   }
}
exit_loop: ;
于 2009-09-14T06:53:40.657 回答
68

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

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

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

当然,这个解决方案只有在 switch 语句之后没有其他代码要执行的情况下才有效。

于 2009-09-14T07:41:52.570 回答
53

前提

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

while( true ) {
}

支持论点

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

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

“转到”的替代方案

以下代码是更好的形式:

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方法中,它允许子类覆盖默认行为(使用嵌入式switchand是一项艰巨的任务goto)。

Python 示例

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

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

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

print 'Break!' 

相对:

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

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

print 'Break!'

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

于 2009-09-14T07:14:53.463 回答
24

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

int yourfunc() {

    while(true) {

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

    }
}

可选(但“不好的做法”):正如已经建议的那样,您可以使用 goto,或者在开关内抛出异常。

于 2009-09-14T06:56:47.500 回答
15

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

于 2009-09-14T06:54:38.767 回答
9

您可以将开关放入一个单独的函数中,如下所示:

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())
    ;
于 2009-09-14T07:35:58.340 回答
7

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

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

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

但我认为 usinggoto是这里唯一的选择,因为 string 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 ...
}
于 2009-09-14T07:15:38.560 回答
5

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

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

于 2009-09-14T06:59:56.430 回答
4

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

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);
于 2017-04-07T06:37:33.463 回答
2

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

于 2009-09-14T06:54:37.457 回答
2

为什么不直接修复 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**
    }
}
于 2013-07-15T15:22:05.037 回答
1

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

bool imLoopin = true;

while(imLoopin) {

    switch(msg->state) {

        case MSGTYPE: // ... 
            break;

        // ... more stuff ...

        case DONE:
            imLoopin = false;
            break;

    }

}

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

于 2011-05-04T07:57:39.783 回答
1

我认为;

while(msg->state != mExit) 
{
    switch(msg->state) 
    {
      case MSGTYPE: // ...
         break;
      case DONE:
      //  .. 
      //  ..
      msg->state =mExit;
      break;
    }
}
if (msg->state ==mExit)
     msg->state =DONE;
于 2014-01-07T08:52:30.537 回答
1

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

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;
}
于 2016-08-08T11:00:50.590 回答
1

因为switch使用break来从switch(而不是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:

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

案例 '','\t':

这将是

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

这就是为什么这里使用的休息时间......

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

标准 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<=c<=5 执行,并且 break 将其从 while 循环中退出。

于 2021-10-06T05:31:36.520 回答
0

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

于 2009-11-12T09:06:38.790 回答
0

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

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

    //...
    }
}

如果您需要在msg->stateequals时执行某些操作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();
于 2011-01-04T15:32:19.813 回答
0
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**
}
}
于 2011-06-22T07:44:28.270 回答
-2

如果我记得 C++ 语法,您可以为break语句添加标签,就像 for 一样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
于 2009-09-14T17:13:56.573 回答
-3
  while(true)
  {
    switch(x)
    {
     case 1:
     {
      break;
     }
    break;
   case 2:
    //some code here
   break;
  default:
  //some code here
  }
}
于 2012-01-30T17:45:04.103 回答