我应该如何从循环中跳出?

How should I break out from a loop?

每当我需要从 C++ 中的 for(unsigned int i=0;i<bound;++i) 表达式中跳出时,我只需设置索引变量 i=bound,方法与 中描述的相同。我倾向于避免使用 break 语句,因为老实说,我不太了解它 实际上 的作用。

比较两条指令:

for(unsigned int i=0;i<bound;++i) {
    if (I need a break) {
    break;
    }
}

for(unsigned int i=0;i<bound;++i) {
    if (I need a break) {
    i=bound;
    }
}

我推测第二种方法多做了一个变量集,然后在ibound之间进行了一次额外的比较,所以看起来更昂贵,从性能的角度。问题是调用 break 然后进行这两个测试是否更便宜?编译后的二进制文件有什么不同吗?是否存在第二种方法失效的情况,或者我可以安全地选择这两种方法中的任何一种吗?


相关:Does `break` work only for `for`, `while`, `do-while`, `switch' and for `if` statements?

我更喜欢break;,因为它完整地保留了循环变量。
我在搜索内容时经常使用这种形式:

int i;
for(i=0; i<list.size(); ++i)
{
    if (list[i] == target) // I found what I'm looking for!
    {
        break;  // Stop searching by ending the loop.
    }
}

if (i == list.size() ) // I still haven't found what I'm looking for -U2
{
    // Not found.
}
else
{
    // Do work with list[i]. 
}

编译后的二进制文件是否不同?
几乎可以肯定是的。
(尽管优化器可能会识别您的模式,并将它们减少到几乎相同)
break; 语句可能是一个汇编 "jump" 语句,用于跳转到列表外的下一条指令,同时保持控制变量不变。

分配变量(在未优化的代码中)将导致分配给控制变量,测试该变量,然后跳转结束循环。

正如其他人所提到的,将变量分配给它的最终值不太适合未来,以防将来您的循环条件发生变化。

一般来说,当你说:
"I have no good understanding of what it actually does. (so I use a workaround)",

我回复:
"Take the time to learn what it does! A main aspect of your job as a programmer is to learn stuff."

使用 break 将更有前瞻性和逻辑性。

考虑以下示例,

for (i = 0; i < NUM_OF_ELEMENTS; i++)
{
     if(data[i] == expected_item)
         break;
} 

printf("\n Element %d is at index %d\n", expected_item, i);

但是第二种方法在这里没有用

使用 break 来执行此操作是惯用的,应该是默认设置,除非出于某种原因,相当模糊的替代方案为下面的逻辑奠定了基础。即便如此,我更愿意在循环退出后进行变量设置,为了清楚起见将该设置移近它的用法。

我无法想象性能问题足以让我担心的场景。也许一个更复杂的例子可以证明这一点。如前所述,答案几乎总是 'measure, then tune'.

除了 break 语句退出 for 或 [do] while 循环外,允许使用 goto 中断嵌套循环,例如:

    for (i=0; i<k; i++) {
        for (j=0; j<l; j++) {
            if (someCondition) {
                goto end_i;
            }
        }
    }
  end_i:

我想到了三个主要的技术差异:

  • 正如其他人所说,如果您的索引变量不局限于 for 范围 break 则保持不变,而您的方法会破坏其内容;当您搜索时一个数组 break 代码更简洁(你不必保留一个额外的变量来写下你停止的地方);
  • break 立即退出循环;您的方法要求您执行 body 的其余部分。当然你可以随时写:

    for(int i=0; i<n; ++i) {
        if(...) {
            i=n;
        } else {
            rest of the loop body
        }
    }
    

    但它会增加循环的视觉和逻辑混乱;

  • break 几乎肯定会被翻译成一个简单的 jmp 到循环之后的指令(尽管,如果你有 block-scoped 变量一个析构函数情况可能更复杂);您的解决方案不一定被编译器识别为等效的。

    你实际上可以看到它 here gcc 一路生成将 n 移动到 i 的代码,而在第二种情况下它直接跳出循环.

在文体方面:

  • 我发现 "your way" 过于复杂而且不符合习惯 - 如果我在实际代码中遇到它,我会问自己 "why didn't he just use a break?",然后检查两次以确保它不像我'我遗漏了一些副作用,其目的是实际上只是为了跳出循环;
  • 正如其他人所说,您的内部分配存在与实际循环条件不同步的风险;
  • 当循环条件变得比简单的范围检查更复杂时,它不会缩放,无论是在逻辑方面(如果循环条件很复杂,那么欺骗它会变得更复杂)和在​​性能方面(如果循环条件很昂贵,你已经知道你想退出你不想再次检查它);这也可以通过添加一个额外的变量来规避(这通常是在缺少 break 的语言中完成的),但这又是对你的算法实际做的事情的额外干扰;
  • 它对 range-based 循环根本不起作用。