for (;;) {
//Something to be done repeatedly
}
我见过这种东西用了很多,但我觉得它很奇怪......说起来会不会更清楚while(true)
,或者类似的东西?
我猜想(这也是许多程序员求助于神秘代码的原因)这是一个很小的差距吗?
为什么,真的值得吗?如果是这样,为什么不这样定义:
#define while(true) for(;;)
for (;;) {
//Something to be done repeatedly
}
我见过这种东西用了很多,但我觉得它很奇怪......说起来会不会更清楚while(true)
,或者类似的东西?
我猜想(这也是许多程序员求助于神秘代码的原因)这是一个很小的差距吗?
为什么,真的值得吗?如果是这样,为什么不这样定义:
#define while(true) for(;;)
我更喜欢for(;;)
有两个原因。
一是一些编译器会产生警告while(true)
(类似于“循环条件是恒定的”)。避免警告总是一件好事。
另一个是我认为for(;;)
更清晰,更有说服力。我想要一个无限循环。它实际上没有条件,它不依赖任何东西。我只是希望它永远持续下去,直到我做点什么来摆脱它。
然而while(true)
,好吧,与任何事情有什么关系?我对循环不感兴趣,直到真变为假,这就是这种形式的字面意思(当真为真时循环)。我只想循环。
不,绝对没有性能差异。
我个人使用for (;;)
是因为其中没有任何数字,它只是一个关键字。我更喜欢while (true)
, while (1)
, while (42)
,while (!0)
等等。
因为丹尼斯·里奇
我开始使用for (;;)
是因为 Dennis Ritchie 在 K&R 中就是这样做的,而且在学习一门新语言时,我总是试图模仿聪明人。
这是惯用的 C/C++。如果您打算在 C/C++ 领域做很多事情,从长远来看,习惯它可能会更好。
你#define
不会工作,因为被#define 的东西必须看起来像一个 C 标识符。
所有现代编译器都会为这两种结构生成相同的代码。
在任何理智的编译器中肯定不会更快。它们都将被编译成无条件跳转。for 版本更容易输入(正如 Neil 所说),如果您了解 for 循环语法,就会很清楚。
如果你很好奇,下面是 gcc 4.4.1 为我提供的 x86 的内容。两者都使用 x86 JMP指令。
void while_infinite()
{
while(1)
{
puts("while");
}
}
void for_infinite()
{
for(;;)
{
puts("for");
}
}
编译为(部分):
.LC0:
.string "while"
.text
.globl while_infinite
.type while_infinite, @function
while_infinite:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
.L2:
movl $.LC0, (%esp)
call puts
jmp .L2
.size while_infinite, .-while_infinite
.section .rodata
.LC1:
.string "for"
.text
.globl for_infinite
.type for_infinite, @function
for_infinite:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
.L5:
movl $.LC1, (%esp)
call puts
jmp .L5
.size for_infinite, .-for_infinite
我更喜欢for (;;)
它,因为它在不同的类 C 语言中是最一致的。
在 C++while (true)
中很好,但在 C 中,您依赖于头文件来定义true
,但这TRUE
也是一个常用的宏。如果while (1)
在 C 和 C++ 以及 JavaScript 中使用它是正确的,但在 Java 或 C# 中使用它是正确的,它们要求循环条件是布尔值,例如while (true)
or while (1 == 1)
。在 PHP 中,关键字不区分大小写,但该语言更喜欢大写TRUE
。
但是,for (;;)
在所有这些语言中总是完全正确的。
我个人更喜欢这个for (;;)
成语(它将编译成与while (TRUE)
.
Usingwhile (TRUE)
在某种意义上可能更具可读性,我决定使用这个for (;;)
成语,因为它很突出。
无限循环结构应该很容易在代码中被注意到或调用,我个人认为这种for (;;)
风格比while (TRUE)
or做得更好while (1)
。
另外,我记得当 while 循环的控制表达式是常量时,一些编译器会发出警告。我认为这种情况不会发生太多,但仅仅是虚假警告的可能性就足以让我想要避免它。
我看到有些人喜欢它,因为他们在这样的地方有一个#define:
#define EVER ;;
这允许他们这样写:
for (EVER)
{
/* blah */
}
怎么样(如果你的语言支持的话):
start:
/* BLAH */
goto start;
生成的机器代码没有区别。
然而,为了逆势而上,我认为 while(TRUE) 形式比 for(;;) 更具可读性和直观性,并且可读性和清晰性是编码指南的重要原因,而不是我的任何原因听说过 for(;;) 方法(我更喜欢将我的编码指南建立在可靠的推理和/或自己的有效性证明上)。
while(true)
使用 Visual Studio 生成警告(条件不变)。我工作过的大多数地方都会编译带有警告作为错误的生产版本。所以
for(;;)
是首选。
不仅是众所周知的模式,而且是 C(和 C++)中的标准习语
如果您的代码由编译器优化,则两者应该相同。为了解释我所说的优化是什么意思,这里有一个用 MSVC 10 编写的示例代码:
int x = 0;
while(true) // for(;;)
{
x +=1;
printf("%d", x);
}
如果您在调试模式下构建它(没有任何优化 (/Od)),反汇编会显示出明显的差异。true
里面的条件有额外的说明while
。
while(true)
00D313A5 mov eax,1 //extra
00D313AA test eax,eax //extra
00D313AC je main+39h (0D313B9h) //extra
{
x +=1;
00D313AE mov eax,dword ptr [x]
00D313B1 add eax,1
00D313B4 mov dword ptr [x],eax
printf("%d", x);
...
}
00D313B7 jmp main+25h (0D313A5h)
for(;;)
{
x +=1;
00D213A5 mov eax,dword ptr [x]
00D213A8 add eax,1
00D213AB mov dword ptr [x],eax
printf("%d", x);
...
}
00D213AE jmp main+25h (0D213A5h)
但是,如果您在发布模式下构建代码(使用默认的Maximize Speed (/O2)),您将获得相同的输出。两个循环都减少为一个跳转指令。
for(;;)
{
x +=1;
01291010 inc esi
printf("%d", x);
...
}
0129101C jmp main+10h (1291010h)
while(true)
{
x +=1;
00311010 inc esi
printf("%d", x);
...
}
0031101C jmp main+10h (311010h)
无论您使用哪种方式,对于一个具有速度优化功能的体面的编译器来说都无关紧要。
这是个人喜好的问题,哪种方式更快。就个人而言,我是一名打字员,在编程期间从不看我的键盘——我可以在我的键盘上触摸打字所有 104 个键。
我发现输入“while (TRUE)”是否更快。
我在心里添加了一些手指运动测量值并将它们加起来。“for(;;)” 有大约 12 个键宽度的向后和第四个移动(在 home 键和键之间,以及在 home 键和 SHIFT 键之间)“while (TRUE)” 有大约 14 个键宽度的移动回来和第四。
但是,在键入后者时,我的错误率大大降低。我在脑海中一次用文字思考,所以我发现输入“nIndex”之类的东西比输入“nIdx”之类的首字母缩写词更快,因为我实际上必须在脑海中拼出字母而不是在脑海中说出它并让我的手指自动- 输入单词(如骑自行车)
(我的 TypingTest.com 基准 = 136 WPM)
我无法想象一个有价值的编译器会生成任何不同的代码。即使是这样,如果不测试更有效的特定编译器,也无法确定。
但是,我建议您更喜欢for(;;)
以下原因:
我使用的许多编译器将使用适当的警告级别设置为 while(true) 生成常量表达式警告。
在您的示例中,宏 TRUE 可能未按您期望的那样定义
无限 while 循环有许多可能的变体,例如while(1)
,while(true)
等while(1==1)
;所以for(;;)
可能会导致更大的一致性。
所有好的答案 - 行为应该完全相同。
但是-假设它确实有所作为。假设其中一个在每次迭代中多执行 3 条指令。
你应该关心吗?
仅当您在循环中所做的几乎没有任何事情时,几乎从未如此。
我的观点是,有微观优化和宏观优化。微优化就像“理发减肥”。
“永远”循环作为后台循环在嵌入式系统中很流行。有些人将其实现为:
for (; ;)
{
// Stuff done in background loop
}
有时它被实现为:
while (TRUE /* or use 1 */)
{
// Stuff done in background loop
}
还有一个实现是:
do
{
// Stuff done in background loop
} while (1 /* or TRUE */);
优化编译器应该为这些片段生成相同或相似的汇编代码。一个重要的注意事项:循环的执行时间不是一个大问题,因为这些循环永远持续下去,更多的时间花在处理部分。
for(;;Sleep(50))
{
// Lots of code
}
比:
while(true)
{
// Lots of code
Sleep(50);
}
如果您不使用Sleep()
.
我假设 while(true) 比 for(;;) 更具可读性——它看起来像程序员在 for 循环中遗漏了一些东西:)
使用“for(;;)”的最重要原因是害怕在进行探索性编程时使用“while(TRUE)”。使用“for”更容易控制重复次数,也更容易将“for”循环转换为无限循环。
例如,如果您正在构造一个递归函数,您可以在转换为无限循环之前限制对该函数的调用量。
for(int i=0;i<1000;i++) recursiveFunction(oneParam);
当我确定我的功能时,我将其转换为无限循环:
for(;;) recursiveFunction(oneParam);
正如其他人所指出的,从技术角度来看,这根本不重要。有些人认为一个比另一个更具可读性,他们对它是什么有不同的看法。
对我来说,这基本上只是吹毛求疵,因为任何 C 程序员都应该能够立即将两者识别while(1)
为for(;;)
无限循环。
您的定义将不起作用。但是,您可以(但不应该)这样做:
#define repeat for(;;)
int main(void)
{
repeat {
puts("Hello, World");
}
}
但真的,不要做那样的事情......