应该在 for 循环中使用 < 或 < = 吗

如果您必须迭代一个循环7次,您会使用:

for (int i = 0; i < 7; i++)

或:

for (int i = 0; i <= 6; i++)

有两点需要考虑:

  • 表演
  • 可读性

就性能而言,我假设是 Java 或 C # 。使用“小于”或“小于或等于”是否重要?如果你对不同的语言有洞察力,请指出哪一种。

为了便于阅读,我假设基于0的数组。

UPD: 我提到的基于0的数组可能会让人感到困惑。我不是在讨论对数组元素进行迭代。只是一般的循环。

下面有一个关于使用常量的好点,它可以解释这个神奇的数字是什么。所以如果我有“ int NUMBER_OF_THINGS = 7”那么“ i <= NUMBER_OF_THINGS - 1”看起来会很奇怪,不是吗。

46554 次浏览

当涉及到性能时,它不会产生有效的差异。因此,我会使用任何更容易理解你正在解决的问题的上下文。

这两个循环都迭代7次。我会说有7的那个更易读/更清晰,除非你有一个非常好的理由来解释另一个。

在 Java 1.5中,你只需要

for (int i: myArray) {
...
}

所以对于数组的情况你不需要担心。

我总是使用 < array.length,因为它比 < = array.length-1更容易阅读。

也有 < 7,并且假设你知道它是从0索引开始的,那么直观地说,这个数字就是迭代次数。

我不认为有什么性能差异。第二种形式肯定更易于阅读,但是,您不必在心里减去一个来找到最后的迭代编号。

编辑: 我看到其他人不同意。就我个人而言,我喜欢在循环结构中看到实际的索引号。也许是因为它更容易让人联想到 Perl 的 0..6语法,我知道它等价于 (0,1,2,3,4,5,6)。如果我看到一个7,我必须检查它旁边的操作符,看到,实际上,索引7从未达到。

我更喜欢:

for (int i = 0; i < 7; i++)

我认为这更容易翻译成“循环迭代7次”。

我不确定这对性能的影响——我怀疑任何差异都会被编译掉。

第一个是更多的 成语。特别是,它表示(从0开始)迭代次数。当使用基于1的东西(例如 JDBC、 IIRC)时,我可能会倾向于使用 < = 。所以:

for (int i=0; i < count; i++) // For 0-based APIs


for (int i=1; i <= count; i++) // For 1-based APIs

我希望在现实世界的代码中,性能差异微不足道。

我认为两者都可以,但是当你选择的时候,要么选择一个,要么选择另一个。如果您习惯于使用 < = ,那么尽量不要使用 < ,反之亦然。

我更喜欢使用 < = ,但是在使用从零开始的索引的情况下,我可能会尝试使用 < 。不过这都是个人喜好。

从优化的角度来看,这并不重要。

从代码样式的角度来看,我更喜欢 < . 原因:

for ( int i = 0; i < array.size(); i++ )

可读性比

for ( int i = 0; i <= array.size() -1; i++ )

Also < 直接给出迭代次数。

对 < 的另一个投票是,您可以防止许多意外的 off-by-one 错误。

我同意大家的说法,7在这种情况下是有意义的,但是我要补充的是,在6很重要的情况下,如果你想表明你只对第6个索引以下的对象进行操作,那么 < = 更好,因为它使得6更容易看到。

在大学的时候,我记得这两个操作在 CPU 上的计算时间是相似的。当然,我们说的是集合层。

然而,如果你谈论的是 C # 或 Java,我真的不认为其中一个会比另一个提高速度,你获得的几纳秒很可能不值得你引入任何困惑。

就我个人而言,我将编写从业务实现的角度来看有意义的代码,并确保它易于阅读。

我建议使用“ < 7”版本,因为大多数人都会阅读这个版本——所以如果人们略读您的代码,他们可能会错误地解释它。

我不会担心“ <”是否比“ < =”快,只是为了可读性。

如果你想提速,考虑以下几点:

for (int i = 0; i < this->GetCount(); i++)
{
// Do something
}

为了提高性能,你可以稍微重新安排一下:

const int count = this->GetCount();
for (int i = 0; i < count; ++i)
{
// Do something
}

注意从循环中删除了 GetCount ()(因为在每个循环中都会查询它) ,并将“ i + +”更改为“ + + i”。

从严格的逻辑观点来看,您必须认为 < count将比 <= count更有效,因为确切的原因是 <=也将测试相等性。

顺便说一句,在.Net 中循环访问数组或其他集合时,我发现

foreach (string item in myarray)
{
System.Console.WriteLine(item);
}

比 for 循环中的数字更具可读性。当然,这是假定循环代码中没有使用实际的计数器 Int 本身。我不知道是否有一个性能变化。

写 i < 7有很多好的理由。在一个循环中迭代7次的数字7是很好的。性能完全相同。几乎每个人都写 i < 7。如果你是为了可读性而写作,那么就使用人人都能马上认出来的形式。

我还记得我们在大学里做8086大会的时候,那时候做的更有表演性:

for (int i = 6; i > -1; i--)

因为有一个 JNS操作,这意味着跳转,如果没有标志。使用这种方法意味着在每个循环之后不需要查找内存来获取比较值,也不需要进行比较。现在大多数编译器都会优化寄存器的使用情况,所以内存不再重要,但是仍然可以进行不必要的比较。

顺便说一下,在你的循环中加入7或6就是引入了一个“ 神奇的数字”。为了更好的可读性,您应该使用具有意图显示名称的常量。像这样:

const int NUMBER_OF_CARS = 7;
for (int i = 0; i < NUMBER_OF_CARS; i++)

编辑: 人们没有得到组装的东西,所以显然需要一个更完整的例子:

如果我们为(i = 0; i < = 10; i + +)做,你需要这样做:

    mov esi, 0
loopStartLabel:
; Do some stuff
inc esi
; Note cmp command on next line
cmp esi, 10
jle exitLoopLabel
jmp loopStartLabel
exitLoopLabel:

如果我们对(int i = 10; i > -1; i -)这样做,那么你可以这样做:

    mov esi, 10
loopStartLabel:
; Do some stuff
dec esi
; Note no cmp command on next line
jns exitLoopLabel
jmp loopStartLabel
exitLoopLabel:

我刚刚检查了一下,微软的 C + + 编译器没有进行这种优化,但是如果你进行了优化,它会进行:

for (int i = 10; i >= 0; i--)

所以,如果你正在使用 Microsoft C + + ,升序或降序没有什么区别,那么你应该使用:

for (int i = 10; i >= 0; i--)

而不是这两者中的任何一个:

for (int i = 10; i > -1; i--)
for (int i = 0; i <= 10; i++)

但坦率地说,获得“ for (int i = 0; i < = 10; i + +)”的可读性通常比缺少一个处理器命令要重要得多。

? 其他编译器可能会执行不同的操作。

我一直更喜欢:

for ( int count = 7 ; count > 0 ; -- count )

至少在 x86编译器中,它的性能不应该有差异。JL 和 JLE 同时工作,一旦我知道。至于可编辑性,对包含7个元素的数组使用“ < 7”是有意义的。

我遵循第一种方法,因为那个成语经常被重复。

For (int index = 0; index < array.length; i + +)

字符串 s = oldString.substring (0,numChars) ;

等等。

我已经习惯了上限被排除在外,而且我更愿意保持这种状态,除非有很好的理由去改变它。(示例——基于1的索引)

首先考虑可读性,然后再进行优化(尽管说实话,我想不出有什么明显的区别)。

请注意,0-> K 形式是一种 C 习惯用法,通过使数组基于0而传递到 C # 中。遵循这个成语,不要违背最不惊讶的原则。

您也可以改用 !=。这样,如果在初始化过程中出现错误,就会得到一个无限循环,导致错误提前被注意到,并且它导致的任何问题都被限制在循环中(而不是在很久之后才发现问题)。

首先,不要用6或7。

最好使用:

int numberOfDays = 7;
for (int day = 0; day < numberOfDays ; day++){


}

在这种情况下,它比使用

for (int day = 0; day <= numberOfDays  - 1; day++){


}

甚至更好(Java/C #) :

for(int day = 0; day < dayArray.Length; i++){


}

甚至更好(C #)

foreach (int day in days){// day : days in Java


}

反向循环确实更快,但是由于它更难读(如果其他程序员不读的话) ,所以最好避免使用它。特别是在 C # ,Java..。

养成使用 < 的习惯将使您和读者在迭代数组时保持一致。每个人都有一个标准的约定会更简单。如果您使用的语言是基于0的数组,那么 < 就是约定。

这几乎肯定比 < 和 < = 之间的性能差异更重要。首先确定功能性和可读性,然后进行优化。

另一个注意事项是,最好养成使用 + + i 而不是 i + + 的习惯,因为提取和增量需要一个临时的和增量,而提取不需要。对于整数,编译器可能会优化临时类型,但是如果迭代类型更复杂,则可能无法优化临时类型。

别用魔法数字。

为什么是7? (或者6)。

使用正确的符号表示您想要使用的数字..。

在这种情况下,我认为最好使用

for ( int i = 0; i < array.size(); i++ )

在 C + + 中,我更喜欢使用 !=,它可以用于所有 STL 容器。并非所有的 STL 容器迭代器都不具有可比性。

’<’和’< =’运算符的性能开销完全相同。

“ <”运算符是标准的,在从零开始的循环中更容易读取。

使用 + + i 而不是 i + + 可以提高 C + + 的性能,但是在 C # 中不行-我不了解 Java。

我更喜欢这样:

for (int i = 0; i < 7; i++)

然而(这只是一个想法) ,它的可读性可能取决于数组是基于0(C # ,Java)还是基于1(VB)。NET).我这么说是因为当你使用基于0的数组时,你会认为0-6会运行7次。我觉得0-6比1-7更直观。然后,我来自一个 C + + ,Java,C # 背景。

对于某些语言/技术,例如。NET 使用。大小或。Length 或 size ()/Length ()是一个坏主意,因为它每次迭代时都会访问该属性,所以将其分配给变量对性能的影响会稍微小一些。

正如人们所观察到的,你提到的两种选择中的任何一种都没有差别。为了证实这一点,我用 JavaScript 做了一些简单的基准测试。

您可以看到结果 给你。不清楚的是,如果我交换第一个和第二个测试的位置,这两个测试的结果交换,这显然是一个记忆问题。然而,第三次测试,其中我逆转了迭代的顺序,显然是更快的。

@ Chris 你关于。长度在。NET 实际上是不正确的,在简单类型的情况下正好相反。

int len = somearray.Length;
for(i = 0; i < len; i++)
{
somearray[i].something();
}

实际上比

for(i = 0; i < somearray.Length; i++)
{
somearray[i].something();
}

后者是由运行时优化的情况。由于运行时可以保证 i 是数组的有效索引,因此不会执行边界检查。在前一种情况下,运行时不能保证在循环之前没有修改 i,并且对每次索引查找都强制对数组进行边界检查。

正如每个人所说的,通常使用0索引迭代器,即使对于数组之外的事情也是如此。如果所有东西都从 0开始,以 n-1结束,并且下界总是 <=,上界总是 <,那么在审查代码时就不需要考虑那么多了。

结果说不通。

从硬件的角度来看,< = with a loop Number-1将在每次迭代中引入一个额外的计算来执行 loop Number-1。所以我假设 < 将花费更少的时间,如果不是与 < = 相同的时间量的话

这直接属于 “让错误的代码看起来错误”的范畴。

在从零开始的索引语言中,比如 Java 或 C # ,人们已经习惯了 index < count条件的变化。因此,利用这种事实上的约定将使得一个错误之外的错误更加明显。

关于性能: 任何优秀的编译器都值得它的内存占用,因此应该呈现一个不存在问题的编译器。

Edsger Dijkstra 在1982年的 写了篇文章中提出了更低的比例:

有一个最小的自然数。排除下界ーー如 b)和 d)ーー从最小自然数开始的子序列的力,如上所述的下界进入非自然数的范围。这很难看,所以对于下界,我们更喜欢 a)和 c 中的≤。现在考虑从最小自然数开始的子序列: 包含上界将迫使后者在序列缩小到空数时变得非自然。这很难看,所以对于上界,我们更喜欢 < as in a)和 d)。我们得出结论,公约 a)是首选的。

过早优化是万恶之源。除非有一个很好的理由去担心 <而不是 <=,否则还是用可读性吧。

没有速度差异,但是 < 在使用基于0的数组的语言中更可能是正确的。此外,如果您想要向下迭代而不是向上迭代,您可以说:

for (i = 7; --i >= 0; ) ...

我认为应该是 < 。

为什么要多用几个词呢。一个测试比两个更容易理解。因此,今后进行单元测试和修改更加容易。

差别很小吗? 是的。但是为什么要在没有必要的情况下增加复杂性呢。

最后,当代码一开始就进行了优化时,您不需要依赖任何优化器或解释器的实现。引用爱因斯坦的话“尽可能简单,但不要简单”。

好问题。我的答案是: 使用 A 型(’<’)

  • 您可以清楚地看到有多少次迭代(7)。
  • 两个端点之间的差异是范围的宽度
  • 字符越少,可读性越高
  • 你更经常有元素的总数 i < strlen(s)而不是 最后一个元素的索引,所以一致性是很重要的。

另一个问题是整个结构。i出现在它的 三次,所以它可能被打错。For-loop 结构说的是 怎么办而不是 该怎么办。我建议采用以下方法:

BOOST_FOREACH(i, IntegerInterval(0,7))

这就比较清楚了,汇编到一模一样的指令等。如果你愿意,可以问我整数间隔的代码。

这么多答案... 但我相信我还有东西要补充。

我的偏好是字面数字清楚地显示 循环中的“ i”值是什么。因此,在迭代从零开始的数组的情况下:

for (int i = 0; i <= array.Length - 1; ++i)

如果你只是在循环,而不是迭代一个数组,从1到7的计数是相当直观的:

for (int i = 1; i <= 7; ++i)

可读性胜过性能,直到您对其进行分析,因为在此之前您可能不知道编译器或运行时将如何处理您的代码。