“ for (; ;)”比“ while (true)”快吗? 如果不是,为什么人们要用它?

for (;;) {
//Something to be done repeatedly
}

这种东西我见过很多次,但我觉得很奇怪..。 难道说 while(true)或者类似的东西不会更清楚吗?

我猜想(这也是许多程序员求助于神秘代码的原因)这会快一点点?

为什么,它值得吗? 如果值得,为什么不这样定义它:

#define while(true) for(;;)

参见: While (1)和 while (2)哪个更快?

47843 次浏览
  1. 不是更快。
  2. 如果您真的关心,可以使用汇编程序输出为您的平台进行编译,并查看。
  3. 没关系。这从来都不重要。随便你怎么写你的无限循环。

在任何正常的编译器中,它当然都不会更快。它们都将被编译成无条件跳跃。For 版本更容易输入(正如 Neil 所说) ,如果您理解了 for 循环语法,那么它将是清晰的。

如果您感到好奇,下面是 gcc4.4.1为 x86提供的内容。

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

这不仅是一个众所周知的模式,而且是 C (和 C + +)中的一个标准习惯用法

我个人使用 for (;;)是因为它没有任何数字,它只是一个关键字。我喜欢它的 while (true)while (1)while (42)while (!0)等等。

“永久”循环作为后台循环在嵌入式系统中很流行,有些人将其实现为:

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 */);

编译器最佳化应该为这些片段生成相同或相似的汇编代码。一个重要的注意事项: 循环的执行时间并不是一个大问题,因为这些循环永远都在进行,而且在处理部分花费的时间更多。

这是个人喜好的问题,哪种方式更快。就我个人而言,我是一个触摸打字员,在编程的时候从来不看键盘——我可以触摸键盘上的所有104个键。

我发现如果更快地输入“ while (TRUE)”。

我在脑子里加了一些手指运动的测量数据,然后把它们加起来。 “ for (;)”有大约12个键宽的回移和第四个键移动(在主键和键之间,以及在主键和 SHIFT 键之间) “ while (TRUE)”有大约14个键宽的后移和第四个键宽。

然而,在输入后者时,我出错的可能性要小得多。我一次用单词思考,所以我发现输入像“ nIndex”这样的东西比输入像“ nIdx”这样的首字母缩略词要快,因为我必须在心里拼出这些字母,而不是在脑子里说出来,让我的手指自动输入单词(就像骑自行车一样)

(My Type ingTest.com 基准测试 = 136 WPM)

在生成的机器代码方面没有区别。

然而,为了反驳这种趋势,我认为 while (TRUE)形式比 for (; ;)形式更具可读性和直观性,可读性和清晰性是编码指南的重要原因,而不是我听说的 for (; ;)方法的任何原因(我更喜欢将我的编码指南建立在可靠的推理和/或有效性的证明上)。

我见过一些人更喜欢它,因为他们在这样的地方定义了 # :

#define EVER ;;

他们可以这样写:

for (EVER)
{
/* blah */
}

我无法想象一个有价值的编译器会生成任何不同的代码。即使是这样,也没有办法在不测试特定编译器的情况下确定哪个更有效。

不过,我建议您选择 for(;;),原因如下:

  • 我使用的一些编译器将生成一个具有适当警告级别设置的 while (true)常量表达式警告。

  • 在您的示例中,宏 TRUE 可能没有按照您的预期进行定义

  • 有许多可能的变种无限而循环,如 while(1)while(true)while(1==1)等; 所以 for(;;)可能会导致更大的一致性。

我更喜欢 for(;;)有两个原因。

一种是一些编译器在 while(true)上产生警告(类似于“循环条件为常数”)。避免警告总是一件好事。

另一个是,我认为 for(;;)更清晰,更有说服力。 我想要一个无限循环。它字面上 已经没有条件,它什么也不依赖。我只是希望它能永远持续下去,直到我做些什么来打破它。

然而对于 while(true)来说,什么是真有什么关系呢?除非 true 变成 false,否则我对循环不感兴趣,这就是这个表单的字面意思(当 true 为 true 时循环)。我只是想循环。

而且,绝对没有性能上的差异。

我个人比较喜欢 for (;;)习惯用法(它将编译成与 while (TRUE)相同的代码。

从某种意义上说,使用 while (TRUE)可能更具可读性,因此我决定使用 for (;;)惯用语,因为 很显眼

无限循环结构应该很容易在代码中被注意到或被调用,我个人认为 for (;;)样式在这方面比 while (TRUE)while (1)要好一些。

另外,我记得有些编译器会在 while 循环的控制表达式为常量时发出警告。我不认为这种情况发生得太多,但仅仅是虚假警告的可能性就足以让我想要避免这种情况发生。

那么(如果你的语言支持的话) :

start:
/* BLAH */
goto start;

因为 Dennis Ritchie

  • 我开始使用 for (;;)是因为 Dennis Ritchie 在 K & R 就是这么做的,当我学习一门新语言的时候,我总是试图模仿那些聪明的家伙。

  • 这是惯用的 C/C + + 。如果您计划在 C/C + + 空间中做很多事情,那么从长远来看,最好还是习惯于使用它。

  • 您的 #define将无法工作,因为被定义的东西必须看起来像一个 C 标识符。

  • 所有现代编译器都将为这两个构造生成相同的代码。

所有好的答案-行为应该是完全一样的。

然而——假设它确实起了作用,假设他们中的一个每次迭代多接受3条指令。

你在乎吗?

只有当你在循环中所做的是 几乎没有,这是 几乎没有的情况。

我的观点是,有微观优化和宏观优化,微观优化就像“理发减肥”。

while(true)

使用 VisualStudio 生成警告(条件为常数)。我工作过的大多数地方都将警告作为错误来编译生产构建。那么

for(;;)

最好。

我假设 while (true)比 for (; ;)更具可读性—— 看起来程序员在 for 循环中遗漏了一些东西:)

我更喜欢 for (;;),因为它在不同的类 C 语言中是最一致的。

在 C + + 中,while (true)是可以的,但是在 C 中,你需要依靠一个头来定义 true,而 TRUE也是一个常用的宏。如果您使用的是 while (1),那么在 C 和 C + + 以及 JavaScript 中是正确的,但是在 Java 或 C # 中则不是,后者要求循环条件为布尔值,比如 while (true)while (1 == 1)。在 PHP 中,关键字不区分大小写,但是该语言倾向于大写 TRUE

然而,在所有这些语言中,for (;;)总是完全正确的。

for(;;Sleep(50))
{
// Lots of code
}

比:

while(true)
{
// Lots of code
Sleep(50);
}

如果您不使用 Sleep(),这并不适用。

使用“ for (; ;)”最重要的原因是害怕在进行探索性编程时使用“ while (TRUE)”。使用“ for”更容易控制重复的次数,也更容易将“ for”循环转换为无限次。

例如,如果正在构造递归函数,则可以在转换为无限循环之前限制对该函数的调用数量。

    for(int i=0;i<1000;i++) recursiveFunction(oneParam);

当我确定我的函数,然后我将它转换成一个无限循环:

    for(;;) recursiveFunction(oneParam);

如果编译器对代码进行了优化,则两者应该相同。为了解释我所说的优化,下面是用 MSVC 10编写的一个示例代码:

int x = 0;


while(true) // for(;;)
{
x +=1;


printf("%d", x);
}

如果是在调试模式(没有任何优化(/Od))下生成,则反汇编显示出明显的差异。在 while中有关于 true条件的额外说明。

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)

但是,如果您在发布模式下构建代码(使用默认的 最大速度(/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)

无论您将使用什么,对于一个具有速度优化的优秀编译器来说都是无关紧要的。

正如其他人指出的那样,从技术角度来看,这根本无关紧要。有些人认为一个比另一个更可读,他们对哪一个有不同的看法。

对我来说,这基本上就是吹毛求疵,因为任何 C 程序员都应该能够将 while(1)for(;;)都识别为无限循环。

你的定义不起作用。然而,你可以(但不应该)这样做:

#define repeat for(;;)


int main(void)
{
repeat {
puts("Hello, World");
}
}

但是说真的,不要做那样的事情..。