Endless loop in C/C++(C/C++ 中的无限循环)
问题描述
无限循环有几种可能,我会选择以下几种:
for(;;) {}
while(1) {}
/while(true) {}
do {} while(1)
/do {} while(true)
是否有某种形式应该选择哪一种?现代编译器是否对中间语句和最后一条语句有所区别,或者它是否意识到这是一个无限循环并完全跳过检查部分?
正如前面提到的,我忘记了 goto
,但这样做是因为我根本不喜欢它作为命令.
Edit2:我对从 kernel.org 获取的最新版本做了一些 grep.随着时间的推移,我似乎没有太大变化(至少在内核中)
问这个问题的问题是你会得到很多主观的答案,只是简单地说明我更喜欢这个......".我不会做出这些毫无意义的陈述,而是尝试用事实和参考资料来回答这个问题,而不是个人意见.
根据经验,我们可以先排除 do-while 替代方案(和 goto),因为它们并不常用.我不记得在由专业人员编写的实时生产代码中看到过它们.
while(1)
、while(true)
和 for(;;)
是实际代码中普遍存在的 3 个不同版本.它们当然是完全等价的,并产生相同的机器代码.
for(;;)
这是永恒循环的原始规范示例.在 Kernighan 和 Ritchie 的古老 C 圣经C 编程语言中,我们可以读到:
K&R 第二版 3.5:
for (;;) {...}
<块引用>
是一个无限"循环,大概会被其他方式打破,例如作为休息或返回.使用 while 还是 for 在很大程度上是一个问题个人喜好.
很长一段时间(但不是永远),这本书被视为经典和 C 语言的定义.由于 K&R 决定展示一个
for(;;)
的例子,至少在 1990 年 C 标准化之前,这被认为是最正确的形式.然而,K&R 自己已经表示这是一个偏好问题.
今天,K&R 是一个非常有问题的来源,用作规范的 C 参考.它不仅多次过时(不是针对 C99 或 C11),而且还宣扬在现代 C 编程中通常被认为是坏的或公然危险的编程实践.
尽管 K&R 是一个可疑的来源,但这一历史方面似乎是支持
for(;;)
的最有力论据.反对
for(;;)
循环的论点是它有点晦涩难懂.要了解代码的作用,您必须了解标准中的以下规则:ISO 9899:2011 6.8.5.3:
for ( clause-1 ; expression-2 ; expression-3 ) 语句
/--/
<块引用>子句 1 和表达式 3 都可以省略.省略的表达式-2由一个非零常数代替.
根据标准中的这段文字,我认为大多数人会同意它不仅晦涩难懂,而且很微妙,因为 for 循环的第一和第三部分在省略时的处理方式与第二部分不同.
while(1)
这应该是比
for(;;)
更易读的形式.然而,它依赖于另一个晦涩但众所周知的规则,即 C 将所有非零表达式视为布尔逻辑真.每个 C 程序员都知道这一点,所以这不是什么大问题.这种形式有一个很大的实际问题,即编译器往往会给出警告:条件总是为真"或类似的.这是一个很好的警告,您真的不想禁用它,因为它对于查找各种错误很有用.例如,当程序员打算编写
while(i == 1)
时,会出现诸如while(i = 1)
之类的错误.此外,外部静态代码分析器可能会抱怨条件始终为真".
while(true)
为了使
while(1)
更具可读性,有些使用while(true)
代替.程序员之间的共识似乎是这是最易读的形式.然而,这种形式与
while(1)
有同样的问题,如上所述:condition is always true"警告.对于 C,这种形式还有另一个缺点,即它使用了 stdbool.h 中的宏
true
.所以为了进行这个编译,我们需要包含一个头文件,这可能不方便.在 C++ 中,这不是问题,因为bool
作为原始数据类型存在,而true
是语言关键字.这种形式的另一个缺点是它使用 C99 bool 类型,该类型仅在现代编译器上可用且不向后兼容.同样,这只是 C 中的问题,而不是 C++ 中的问题.
那么使用哪种形式?两者似乎都不完美.正如 K&R 在黑暗时代已经说过的那样,这是个人喜好问题.
就个人而言,我总是使用 for(;;)
只是为了避免其他形式经常生成的编译器/分析器警告.但也许更重要的是因为这个:
如果连 C 初学者都知道 for(;;)
意味着一个永恒的循环,那么你想让谁的代码更易读?
我想这就是真正归结起来的原因.如果您发现自己试图让您的源代码对于那些甚至不了解编程语言的基本部分的非程序员来说是可读的,那么您只是在浪费时间.他们不应该阅读您的代码.
而且由于每个应该阅读你的代码的人都已经知道 for(;;)
的意思,所以没有必要让它进一步可读 - 它已经是可读.
There are several possibilities to do an endless loop, here are a few I would choose:
for(;;) {}
while(1) {}
/while(true) {}
do {} while(1)
/do {} while(true)
Is there a certain form which one should choose? And do modern compilers make a difference between the middle and the last statement or does it realize that it is an endless loop and skips the checking part entirely?
Edit: as it has been mentioned I forgot goto
, but this was done out of the reason that I don't like it as a command at all.
Edit2: I made some grep on the latest versions taken from kernel.org. I does seems as nothing much changed over time (within the Kernel at least)
The problem with asking this question is that you'll get so many subjective answers that simply state "I prefer this...". Instead of making such pointless statements, I'll try to answer this question with facts and references, rather than personal opinions.
Through experience, we can probably start by excluding the do-while alternatives (and the goto), as they are not commonly used. I can't recall ever seeing them in live production code, written by professionals.
The while(1)
, while(true)
and for(;;)
are the 3 different versions commonly existing in real code. They are of course completely equivalent and results in the same machine code.
for(;;)
This is the original, canonical example of an eternal loop. In the ancient C bible The C Programming Language by Kernighan and Ritchie, we can read that:
K&R 2nd ed 3.5:
for (;;) { ... }
is an "infinite" loop, presumably to be broken by other means, such as a break or return. Whether to use while or for is largely a matter of personal preference.
For a long while (but not forever), this book was regarded as canon and the very definition of the C language. Since K&R decided to show an example of
for(;;)
, this would have been regarded as the most correct form at least up until the C standardization in 1990.However, K&R themselves already stated that it was a matter of preference.
And today, K&R is a very questionable source to use as a canonical C reference. Not only is it outdated several times over (not addressing C99 nor C11), it also preaches programming practices that are often regarded as bad or blatantly dangerous in modern C programming.
But despite K&R being a questionable source, this historical aspect seems to be the strongest argument in favour of the
for(;;)
.The argument against the
for(;;)
loop is that it is somewhat obscure and unreadable. To understand what the code does, you must know the following rule from the standard:ISO 9899:2011 6.8.5.3:
for ( clause-1 ; expression-2 ; expression-3 ) statement
/--/
Both clause-1 and expression-3 can be omitted. An omitted expression-2 is replaced by a nonzero constant.
Based on this text from the standard, I think most will agree that it is not only obscure, it is subtle as well, since the 1st and 3rd part of the for loop are treated differently than the 2nd, when omitted.
while(1)
This is supposedly a more readable form than
for(;;)
. However, it relies on another obscure, although well-known rule, namely that C treats all non-zero expressions as boolean logical true. Every C programmer is aware of that, so it is not likely a big issue.There is one big, practical problem with this form, namely that compilers tend to give a warning for it: "condition is always true" or similar. That is a good warning, of a kind which you really don't want to disable, because it is useful for finding various bugs. For example a bug such as
while(i = 1)
, when the programmer intended to writewhile(i == 1)
.Also, external static code analysers are likely to whine about "condition is always true".
while(true)
To make
while(1)
even more readable, some usewhile(true)
instead. The consensus among programmers seem to be that this is the most readable form.However, this form has the same problem as
while(1)
, as described above: "condition is always true" warnings.When it comes to C, this form has another disadvantage, namely that it uses the macro
true
from stdbool.h. So in order to make this compile, we need to include a header file, which may or may not be inconvenient. In C++ this isn't an issue, sincebool
exists as a primitive data type andtrue
is a language keyword.Yet another disadvantage of this form is that it uses the C99 bool type, which is only available on modern compilers and not backwards compatible. Again, this is only an issue in C and not in C++.
So which form to use? Neither seems perfect. It is, as K&R already said back in the dark ages, a matter of personal preference.
Personally, I always use for(;;)
just to avoid the compiler/analyser warnings frequently generated by the other forms. But perhaps more importantly because of this:
If even a C beginner knows that for(;;)
means an eternal loop, then who are you trying to make the code more readable for?
I guess that's what it all really boils down to. If you find yourself trying to make your source code readable for non-programmers, who don't even know the fundamental parts of the programming language, then you are only wasting time. They should not be reading your code.
And since everyone who should be reading your code already knows what for(;;)
means, there is no point in making it further readable - it is already as readable as it gets.
这篇关于C/C++ 中的无限循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:C/C++ 中的无限循环
基础教程推荐
- 您如何将 CreateThread 用于属于类成员的函数? 2021-01-01
- 什么是T&&(双与号)在 C++11 中是什么意思? 2022-11-04
- 运算符重载的基本规则和习语是什么? 2022-10-31
- 调用std::Package_TASK::Get_Future()时可能出现争用情况 2022-12-17
- C++,'if' 表达式中的变量声明 2021-01-01
- C++ 程序在执行 std::string 分配时总是崩溃 2022-01-01
- 如何定义双括号/双迭代器运算符,类似于向量的向量? 2022-01-01
- 如何在 C++ 中处理或避免堆栈溢出 2022-01-01
- C++ 标准:取消引用 NULL 指针以获取引用? 2021-01-01
- 设计字符串本地化的最佳方法 2022-01-01