我应该如何从循环中跳出?
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;
}
}
我推测第二种方法多做了一个变量集,然后在i
和bound
之间进行了一次额外的比较,所以看起来更昂贵,从性能的角度。问题是调用 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 循环根本不起作用。
每当我需要从 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;
}
}
我推测第二种方法多做了一个变量集,然后在i
和bound
之间进行了一次额外的比较,所以看起来更昂贵,从性能的角度。问题是调用 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 循环根本不起作用。