我读/写过的大多数for循环都是从0开始的,公平地说,我读过的大多数代码都是用于嵌入式系统的,而且都是用C/C ++编写的。在嵌入式系统中,在某些情况下,可读性不如代码效率重要。因此,我不确定以下哪种情况会是更好的选择:
- 第1版**
for(i = 0; i < allowedNumberOfIteration; i++)
{
//something that may take from 1 iteration to allowedNumberOfIteration before it happens
if(somethingHappened)
{
if(i + 1 > maxIteration)
{
maxIteration = i + 1;
}
}
}
- 第2版**
for(i = 1; i <= allowedNumberOfIteration; i++)
{
//something that may take from 1 iteration to allowedNumberOfIteration before it happens
if(somethingHappened)
{
if(i > maxIteration)
{
maxIteration = i;
}
}
}
- 为什么我认为第一版更好:**
1.大多数循环都是从0开始的。所以,有经验的程序员可能会发现从0开始会更好。
- 为什么我认为第二个版本更好:**
1.公平地说,如果函数中有一个从0开始的数组就很好了,因为数组的索引是从0开始的,但是在这部分代码中没有使用数组。
1.除此之外,第二个版本看起来更简单,因为你不必考虑'+1'。
"我不知道的事"
1)是否存在性能差异?
2)哪个版本更好?
3)在决定起点时,还有其他方面需要考虑吗?
4)我是不是太担心了?
3条答案
按热度按时间fcy6dtqo1#
1)没有
2)都不是
3)C和C++中的数组是从零开始的。
4)是的。
jvidinwx2#
C++中所有形式的数组都是 * 从零开始 * 的。也就是说,它们的索引从零开始,直到数组的大小 * 减一 *。例如,一个五个元素的数组的索引为
0
到4
(包括)。至于你的问题列表,对于 1,* 可能 * 会有性能差异。如果你从
1
开始一个循环,那么你可能需要在每个迭代器中减去1
,如果你使用这个值作为数组索引的话。或者如果你增加数组的大小,那么你会使用更多的内存。对于 2 来说,这取决于你迭代的对象,如果是数组索引,那么从零开始的循环显然更好,但是你可能需要从 any 值开始循环,这取决于你要做什么,以及你要解决的问题。
对于 3,您需要考虑的是您 * 使用 * 循环的目的。
和 4,也许有一点。)
kpbwa7wx3#
这个论点来自著名计算机科学家Dijkstra发表的一篇3页的小论文(Dijkstra算法的论文)。
索引2、3、...、12有4种可能性。
a.)
2 <= i < 13
B.)
1 < i <= 12
c.)
2 <= i <= 12
d.)
1 < i < 13
他提到a.)和B.)的优点是两个边界的差等于序列中元素的个数,他还提到如果两个序列相邻,一个序列的上边界等于另一个序列的下边界,他说这无助于在a.)和b.)之间做出决定,所以他将重新开始。
他立即从列表中删除B.)和d.),因为如果我们从零开始一个自然序列,它们的边界将在自然数(-1)之外,这是“丑陋的”。他通过说我们更喜欢
<=
作为下限来完成观察--留给我们a.)和c.)。对于一个空集,他注意到在B.)和c.)中将有-1作为它的上界,这也是“丑陋的”。
所有这三个观察结果都导致了用a表示自然数序列的约定。),这确实是大多数人编写遍历数组的
for
的方法:我们包括下限(i <= 0
),并且我们排除上限(i < size
)。如果你使用
for(int i = 0; i <= iterations - 1; ++i)
这样的东西来做i
迭代,你可以看到他在空集的情况下提到的丑陋。iterations - 1
对于零迭代将是-1
。因此,按照惯例,我们使用a.),并且由于索引数组为零,我们从
i = 0
开始大量的for
循环。然后,我们推理简约--如果没有其他理由以不同的方式做一件事或另一件事,不妨以完全相同的方式做不同的事情。现在,如果我们使用a.),从1开始索引数组,而不是从0开始索引数组,我们将得到
for(int i = 1; i < size + 1; ++i)
。+ 1
很“难看”,所以我们更喜欢从i = 0
开始我们的范围。总之,你应该用
for(int i = 0; i < iterations; ++i)
执行for
iterations
次。类似for(int i = 1; i <= iterations; ++i)
的东西是相当容易理解的,也是有效的,但是有什么好的理由来添加一种不同的方式来循环iterations
次呢?只要使用索引数组时的相同模式就行了。换句话说,使用0 <= i < size
。你不必担心太多。像热爱自己行业的工匠一样调整自己的风格是一个伟大的程序员所立足的基础。追求简约和按照别人倾向于写代码的方式写代码(包括你自己--数组的循环!)都是明智的,值得追求的伟大事情。
由于这个约定,当我看到
for(i = 1
时,我注意到了一个约定的偏离。然后我对那个代码更加谨慎,认为for
中的逻辑可能依赖于从1
而不是0
开始。这是轻微的,但是当一个约定被广泛使用时,没有理由添加这种可能性,如果您碰巧有一个大的for
体,这种抱怨就不那么轻微了。要理解为什么从一开始就没有意义,考虑一下把这个论证带到它的自然结论--“但它对我来说有意义!"的论证:你可以从任何地方开始
i
!如果我们摆脱了约定,为什么不循环for(int i = 5; i <= iterations + 4; ++i)
?或者for(int i = -5; i > -iterations - 5; --i)
?就像大多数程序员在大多数情况下做的那样,并且在有充分理由的情况下保存差异性--差异性向阅读您代码的程序员发出信号,表明for
的主体包含一些不寻常的东西。通过标准的方法,我们知道for
要么是索引/排序/用从0开始的序列进行算术运算,要么是连续执行某个逻辑iterations
次。请注意这个约定是多么的普遍,在C++中,每个标准容器都在
[start, end)
之间迭代,它对应于上面的a.),这样,结束条件就可以是iter != end
。但是,我们已经用一种方式来做逻辑,而且这种方式没有直接的缺点,这一事实自然地流入了“为什么我们在这个上下文中已经这样做了,还要用两种不同的方式呢?”在他的小论文中,Dijkstra还注意到一种名为梅萨的语言,它可以用特定的语法做a.),B.),c.)或d.)。他声称,在那里,a.)在实践中取得了胜利,而其他的则与bug的原因有关。然后,他哀叹FORTRAN如何在1上索引,以及PASCAL如何按照惯例采用C。)。