我几乎从未见过这样的for
循环:
for (int i = 0; 5 != i; ++i)
{}
在循环中加1时是否有使用>
或<
代替的技术原因?还是这更像是惯例?!=
for
5 != i
是邪恶的
if ( i == 5 ) ...
和if ( i = 5 )
。完全不同的东西。反转操作数可以避免此问题:由于文字不是lvals
,因此无法将它们赋值,并且编译器将输入错字。@Zingam:好的防御性编程!
我几乎从未见过这样的for
循环:
for (int i = 0; 5 != i; ++i)
{}
在循环中加1时是否有使用>
或<
代替的技术原因?还是这更像是惯例?!=
for
5 != i
是邪恶的
if ( i == 5 ) ...
和if ( i = 5 )
。完全不同的东西。反转操作数可以避免此问题:由于文字不是lvals
,因此无法将它们赋值,并且编译器将输入错字。@Zingam:好的防御性编程!
Answers:
while (time != 6:30pm) {
Work();
}
现在是下午6:31 ...该死,现在我下一次回家的机会是明天!:)
这表明较强的限制可以减轻风险,并且可能更直观地理解。
<
将隐藏该错误,但使用!=
则会明显表明存在问题。
是的,这是有原因的。如果您这样编写(基于普通旧索引的)for循环
for (int i = a; i < b; ++i){}
那么它对于任何 a
和b
(即零迭代时a > b
,如果你使用过的,而不是无限i == b;
)。
另一方面,对于迭代器,您需要编写
for (auto it = begin; it != end; ++it)
因为任何迭代器都应实现一个 operator!=
,但并非每个迭代器都可以提供operator<
。
也是基于范围的for循环
for (auto e : v)
不仅是花哨的糖,而且可测量地减少了编写错误代码的机会。
!=
代替的案例感兴趣<
。我个人从未使用过我的想法。
for (int i=a; i != b; i++) {}
!=
在迭代器上使用除之外的任何东西都是非常危险的,因为有时迭代器可以小于比较(例如,它是一个指针),但是比较是没有意义的(它是一个链表)。
你可以有类似的东西
for(int i = 0; i<5; ++i){
...
if(...) i++;
...
}
如果您的循环变量是由内部代码编写的,则i!=5
可能不会破坏该循环。检查不平等现象更安全。
编辑可读性。不平等形式使用得更频繁。因此,由于没有什么特别需要理解的内容,因此阅读起来非常快(因为任务很常见,因此可以减轻大脑负担)。因此,读者利用这些习惯很酷。
最后但并非最不重要的是,这称为防御性编程,这意味着始终采用最强的情况,以避免当前和将来的错误影响程序。
唯一不需要防御性编程的情况是状态已通过前置条件和后置条件证明(但随后证明这是所有编程中最具防御性的)。
当您最经常使用!=
符号时,迭代器是重要的情况:
for(auto it = vector.begin(); it != vector.end(); ++it) {
// do stuff
}
授予:实际上,我会根据编写相同的内容range-for
:
for(auto & item : vector) {
// do stuff
}
但重点仍然是:通常使用==
或比较迭代器!=
。
!=
它们。例如,在a中,std::vector
您可以使用<
迭代器绑定到索引/指针,但是在a中std::set
,没有严格的排序,因为它遍历了二叉树。
循环条件是强制循环不变式。
假设您没有看循环的主体:
for (int i = 0; i != 5; ++i)
{
// ?
}
在这种情况下,您知道在循环迭代开始时i
不等于5
。
for (int i = 0; i < 5; ++i)
{
// ?
}
在这种情况下,您知道在循环迭代开始时i
小于5
。
第二个比第一个多得多的信息,不是吗?现在,程序员的意图(几乎可以肯定)是相同的,但是如果您正在寻找错误,那么从阅读一行代码中获得自信是一件好事。第二个强制执行该不变性,这意味着在第二种情况下不会发生(或者不会导致内存损坏)的某些错误在第一种情况下会叮咬您。
与<
相比,通过阅读更少的代码,您可以了解更多有关程序状态的信息!=
。在现代CPU上,它们花费的时间相同,没有差异。
如果您i
没有在循环体操作,并且它总是增加1,并且比开始少5
,则不会有任何区别。但是,要知道它是否被操纵,您必须确认所有这些事实。
其中一些事实相对容易,但您可能会出错。但是,检查循环的整个过程很痛苦。
在C ++中,您可以编写如下indexes
类型:
for( const int i : indexes(0, 5) )
{
// ?
}
与上述两个for
循环中的任何一个执行相同的操作,甚至由编译器将其优化为相同的代码。但在这里,你知道是i
不能在循环体被操纵,因为它被宣布const
,而代码破坏内存。
在无需理解上下文的情况下,可以从代码行中获得的信息越多,越容易找出问题所在。 <
在整数循环的情况下,与该行相比,!=
它为您提供了关于该行代码状态的更多信息。
可能会将变量i
设置为较大的值,如果仅使用!=
运算符,则将陷入无限循环。
i
当C 达到最大值时,它们会默默地回绕。但是,是的,很可能比您准备等待的时间更长。
我用形容词“技术性”来表示语言行为/怪癖和编译器副作用,例如生成代码的性能。
为此,答案是:no(*)。(*)是“请查阅您的处理器手册”。如果使用的是边缘情况下的RISC或FPGA系统,则可能需要检查生成了哪些指令以及它们的成本。但是,如果你正在使用几乎任何传统的现代建筑,那么在成本没有显著处理器级差之间lt
,eq
,ne
和gt
。
如果您使用的是边缘的情况下,你可能会发现,!=
需要三个操作(cmp
,not
,beq
)与两(cmp
,blt xtr myo
)。同样,在这种情况下,RTM。
在大多数情况下,原因是防御性/强化性,尤其是在处理指针或复杂循环时。考虑
// highly contrived example
size_t count_chars(char c, const char* str, size_t len) {
size_t count = 0;
bool quoted = false;
const char* p = str;
while (p != str + len) {
if (*p == '"') {
quote = !quote;
++p;
}
if (*(p++) == c && !quoted)
++count;
}
return count;
}
一个不太人为的示例是您使用返回值执行增量,并从用户那里接收数据:
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i != len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
i += step; // here for emphasis, it could go in the for(;;)
}
}
尝试此操作,然后输入值1、2、10、999。
您可以防止这种情况:
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i != len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
if (step + i > len)
std::cout << "too much.\n";
else
i += step;
}
}
但是你可能想要的是
#include <iostream>
int main() {
size_t len = 5, step;
for (size_t i = 0; i < len; ) {
std::cout << "i = " << i << ", step? " << std::flush;
std::cin >> step;
i += step;
}
}
还有一些约定偏向于<
,因为在标准容器中的排序通常依赖于operator<
,例如,在多个STL容器中的散列通过说
if (lhs < rhs) // T.operator <
lessthan
else if (rhs < lhs) // T.operator < again
greaterthan
else
equal
如果lhs
和rhs
是用户定义的类,则将该代码编写为
if (lhs < rhs) // requires T.operator<
lessthan
else if (lhs > rhs) // requires T.operator>
greaterthan
else
equal
实现者必须提供两个比较功能。因此<
已成为偏爱的运营商。
通常有几种方法可以编写任何类型的代码,在这种情况下,碰巧只有两种方法(如果计数<=和> =则为三种)。
在这种情况下,人们倾向于使用>和<来确保即使循环中发生了意外事件(例如错误),也不会无限循环(BAD)。例如,考虑以下代码。
for (int i = 1; i != 3; i++) {
//More Code
i = 5; //OOPS! MISTAKE!
//More Code
}
如果我们使用(i <3),我们将不受无限循环的影响,因为它施加了更大的限制。
确实是您要选择的是要让程序中的错误关闭整个程序还是使其中的错误继续起作用。
希望这对您有所帮助!
最常见的使用原因 <
是约定。越来越多的程序员将这样的循环视为“当索引处于范围内时”,而不是“直到索引到达末尾”。可以的话,坚持惯例是有价值的。
另一方面,此处的许多答案都声称使用该<
表单有助于避免错误。我认为在许多情况下,这仅有助于隐藏错误。如果应该使循环索引达到最终值,而实际上却超出了该范围,那么您就没有想到可能会发生故障(或者是其他错误的副作用)。这<
可能会延迟错误的发现。这样!=
做更有可能导致停转,挂起甚至崩溃,这将帮助您更快地发现错误。越早发现错误,修复起来越便宜。
请注意,此约定是数组和向量索引所特有的。当遍历几乎任何其他类型的数据结构时,您将使用迭代器(或指针)并直接检查结束值。在这些情况下,您必须确保迭代器可以达到且不会超出实际最终值。
例如,如果要遍历纯C字符串,通常更常见的是编写:
for (char *p = foo; *p != '\0'; ++p) {
// do something with *p
}
比
int length = strlen(foo);
for (int i = 0; i < length; ++i) {
// do something with foo[i]
}
一方面,如果字符串很长,则第二种形式会变慢,因为这strlen
是另一种通过字符串的方式。
使用C ++ std :: string,即使长度很容易就可以使用基于范围的for循环,标准算法或迭代器。如果您使用的是迭代器,则约定使用!=
而不是<
,如下所示:
for (auto it = foo.begin(); it != foo.end(); ++it) { ... }
同样,对树或列表或双端队列进行迭代通常涉及监视空指针或其他标记,而不是检查索引是否仍在范围内。
不使用此构造的一个原因是浮点数。!=
与float一起使用是非常危险的比较,因为即使数字看起来相同,它也很少会评估为true。<
或>
消除这种风险。
!=
vs <
是最少的问题。
遵循这种做法有两个相关的原因,两者都与一个事实有关,即编程语言毕竟是一种人类(以及其他人)可以阅读的语言。
(1)有点冗余。用自然语言,我们通常会提供比严格必要的更多的信息,这很像一个纠错码。这里的额外信息是循环变量i
(请参阅我在这里如何使用冗余?如果您不知道“循环变量”是什么意思,或者如果忘记了变量的名称,则在阅读“循环变量i
”之后,您将拥有完整的信息)在循环中小于5,不仅与5不同。冗余提高了可读性。
(2)公约。语言具有表达特定情况的特定标准方法。如果您不遵循既定的说话方式,那么您仍然会被理解,但是由于某些优化无法奏效,因此邮件接收者会付出更大的努力。例:
不要谈论热糊状食物。只是说明困难!
第一句话是德语成语的字面翻译。第二个是常见的英语习语,主要单词被同义词代替。结果是可理解的,但要花更长的时间理解:
不要在灌木丛中跳动。只是说明问题!
即使第一版中使用的同义词比英语习语中的常规单词更适合这种情况,也是如此。当程序员阅读代码时,类似的作用也起作用。这也是为什么5 != i
和5 > i
是把它的怪异方式,除非你是在它是标准的交换更加正常的环境中工作i != 5
,并i < 5
以这种方式。这样的方言社区确实存在,可能是因为一致性使记起来更容易记起来,5 == i
而不是自然而容易出错的记号i == 5
。
i < 17+(36/-3)
(即使这些是在其他地方使用的常量;为了清楚起见,您也必须写出它们的名称!)。
除了示例(循环变量将在体内发生(无意地)更改)的示例外,还有其他一些原因可以使用小于或大于运算符:
<
或>
只有一个字符,但!=
两个5 != $i
,这花了我一生的时间。呃…… 窃笑
除了提到它可以减轻风险的各种人员外,它还减少了与各种标准库组件进行交互所需的函数重载次数。举例来说,如果您希望您的类型可存储在中std::set
,或用作的键std::map
,或与某些搜索和排序算法一起使用,则标准库通常用于std::less
比较对象,因为大多数算法只需要严格的弱排序即可。因此,使用<
比较而不是!=
比较(当然是有意义的)成为一种好习惯。
!=
运算符,但并非总是可以定义严格的弱排序。从这个意义上讲!=
会更好,因为它对类型的要求更少。但是,我仍然留在<
。
从语法角度来看没有问题,但是该表达式背后的逻辑5!=i
并不完善。
在我看来,使用!=
for设置for循环的边界在逻辑上是不合理的,因为for循环会增加或减少迭代索引,因此将循环设置为迭代直到迭代索引变得越界(超出!=
某个范围)并不是一个合理的选择。正确实施。
它的工作,但很容易出现不当行为,因为边界数据处理使用时,会丢失!=
一个增量的问题(这意味着你从一开始就知道这是否增加或减少),这就是为什么,而不是!=
在<>>==>
被使用。
i
是5
退出循环。您是说其他话吗?
i++
为i+=2
(例如),它将运行很长时间(或可能永远)。现在,由于通常<
用于将迭代器增加1 以上<
的情况,因此也可以用于将迭代器增加1 的情况(出于一致性考虑)。