我应该如何摆脱循环?

时间:2016-05-05 14:34:25

标签: c++ c performance break

每当我需要从C ++中的for(unsigned int i=0;i<bound;++i)表达式中突破时,我只需设置索引变量i=bound,方法与此answer中描述的方法相同。我倾向于避免使用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?

Breaking out of a loop without a break statement [C]

5 个答案:

答案 0 :(得分:6)

使用 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);

但第二种方法在这里没用。

答案 1 :(得分:4)

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

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

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

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

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

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

在风格方面:

  • 我发现“你的方式”过于复杂而不是惯用 - 如果我在实际代码中遇到它,我会问自己“为什么他不只是使用break?”,然后检查两次确保它不像我错过了一些副作用,并且意图实际只是为了跳出循环;
  • 正如其他人所说,你的内部任务存在一些与实际循环条件不同步的风险;
  • 当循环条件变得比简单范围检查更复杂时,它不会缩放,在逻辑方面(如果循环条件复杂然后欺骗它会变得更复杂)和性能方面(如果循环)条件很贵,你已经知道你要退出你不想再检查一下);通过添加一个额外的变量(通常在缺少break的语言中完成)也可以避免这种情况,但这又会让你的算法实际上更加分散注意力;
  • 它完全不适用于基于范围的循环。

答案 2 :(得分:3)

我更喜欢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”语句,跳转到列表外的下一条指令,同时保持控制变量不变。

分配变量(在非优化代码中)将导致对控制变量的赋值,对该变量的测试,然后是结果跳转以结束循环。

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

一般来说,当你说:
“我对它实际上的作用并不了解。(所以我使用了一种解决方法)”

我回答:
“花点时间了解它的作用!作为程序员,你工作的一个主要方面是学习的东西。”

答案 3 :(得分:3)

使用public partial class CustomerCall { public int Id { get; set; } [StringLength(50)] public string CustomerName { get; set; } [StringLength(50)] public string Subject { get; set; } [Column(TypeName = "text")] public string Comment { get; set; } public DateTime? CallDate { get; set; } public int? Status { get; set; } public int? AssignedTo { get; set; } public DateTime? CreateDate { get; set; } [ForeignKey("Status")] public CallStatus CallStatus { get; set; } } public partial class CallStatus { [Key] public int Id { get; set; } [StringLength(25)] public string StatusName { get; set; } } 执行此操作是惯用的,并且应该是默认的,除非由于某种原因,相当混淆的替代方法用于为下面的逻辑设置阶段。即便如此,我仍然希望在循环退出后进行变量设置,为了清晰起见,将该设置更接近其使用。

我无法想象这样一种情况,即性能是否足以让人担心。也许一个更复杂的例子会证明这一点。如上所述,答案几乎总是“衡量,然后调整”。

答案 4 :(得分:1)

break语句退出for或[do] while循环后,允许使用goto来嵌套嵌套循环,例如:

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