“for(;;)”比“while (true)”快吗?如果不是,人们为啥要使用它?
Posted
技术标签:
【中文标题】“for(;;)”比“while (true)”快吗?如果不是,人们为啥要使用它?【英文标题】:Is "for(;;)" faster than "while (true)"? If not, why do people use it?“for(;;)”比“while (true)”快吗?如果不是,人们为什么要使用它? 【发布时间】:2011-02-06 08:42:42 【问题描述】:for (;;)
//Something to be done repeatedly
我见过这种东西用的很多,但我觉得它很奇怪......
说while(true)
或类似的东西不是更清楚吗?
我猜(这也是许多程序员求助于神秘代码的原因)这是一个很小的差距?
为什么,它真的值得吗?如果是这样,为什么不这样定义:
#define while(true) for(;;)
另见:Which is faster: while(1) or while(2)?
【问题讨论】:
什么是TRUE
???
@Steven Sudit:TRUE
与 C 有什么关系? C99 中真正布尔结果的标准宏仍然是true
。 TRUE
是从哪里来的?
那个宏不起作用,#define EVER ;;
虽然已经在 IOCCC 中使用了.. :)
不是#define while(TRUE)
的宏声明了一个宏,它接受一个从未使用过的名为TRUE
的参数,因此将程序中的每个while 循环都变成了无限循环?跨度>
@Steven Sudit:不。没有“VS 6.0 中的标准宏”。宏 TRUE
来自与 Windows API 关联的头文件。它与 VS 6.0 没有任何关系。而且,正如我之前所说,它与 C 或 C++ 无关。在 C++ 中,“真正的”文字只是 true
(在 VS 6.0 中也是如此),在 C89/90 中根本没有这样的东西,而在 C99 中它是宏 true
。这适用于所有 C/C++ 编译器。
【参考方案1】:
-
它并不快。
如果您真的很在意,请使用适合您平台的汇编器输出进行编译,然后看看。
没关系。这一点都不重要。随心所欲地编写无限循环。
【讨论】:
直观地说,优化无限循环提供了无限加速的潜力。幸好我们不是凭直觉编程的。 :-) 这很重要,因为某些平台上的一些早期编译器确实加载了一个常量并对其进行了条件跳转。在那些日子里,哪个硬件很重要。 这对人类很重要。 Why you shouldn't write while(2) 我使用 for(;;) 因为 while(true) 会生成关于常量条件表达式的警告。【参考方案2】:在任何健全的编译器中肯定不会更快。它们都将被编译成无条件跳转。 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_infinite
更快; puts
少 2 个字符。【参考方案3】:
不仅是众所周知的模式,还是 C(和 C++)中的标准习语
【讨论】:
是的,我认为某些编译器会在while (true)
上发出警告,原因与在 if (true)
上的原因相同。【参考方案4】:
我个人使用for (;;)
,因为其中没有任何数字,它只是一个关键字。我更喜欢while (true)
、while (1)
、while (42)
、while (!0)
等。
【讨论】:
0、1 和无穷大是可接受的幻数,将 0 和 1 扩展为 false 和 true 不是损失。true
不再是一个神奇的数字,正如 for
是一个神奇的关键字或 for(;;)
使用隐含的神奇无穷大。 (while (42)
确实令人憎恶。)
Tangent:我的一位 CS 教授说“编程中只有三个数字:0、1 和 n。其他任何数字都称为幻数。”
while(42) 是最酷的,与 while(1) 或 while(true) 或 while(!0) 产生的结果没有什么不同,并且具有哲学的含义.我想for(;;)
的解析速度无论如何都比其他人快
@ShinTakezou 比 #define LIFE 42
while (LIFE)
好多了:)
好吧while(true)
也不包含任何数字。而 for(;;) 不是关键字【参考方案5】:
“永远”循环作为后台循环在嵌入式系统中很流行。有些人将其实现为:
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 */);
优化编译器应该为这些片段生成相同或相似的汇编代码。一个重要的注意事项:循环的执行时间不是一个大问题,因为这些循环会永远持续下去,而且更多的时间会花在处理部分。
【讨论】:
如果编译器为它们生成不同的代码,是时候放弃编译器并从过去二十年中获取一个。 “循环的执行时间不是一个大问题,因为这些循环会永远持续下去”——这对我来说听起来很不对.. @Blindy -- 因为它是错误的,所以需要关注的是运行时处理循环逻辑所花费的比例,或者每单位有用工作的循环逻辑量,循环都没有帮助是无限的【参考方案6】:这取决于个人喜好,哪种方式更快。就个人而言,我是一名触控打字员,在编程期间从不看我的键盘——我可以在我的键盘上触控打字所有 104 个键。
我发现输入“while (TRUE)”是否更快。
我在心里添加了一些手指运动测量值并将它们加起来。 "for(;;)" 有大约 12 个键宽度的前后移动(在 home 键和键之间,以及在 home 键和 SHIFT 键之间) "while (TRUE)" 有大约 14 个键宽度的前后移动。
但是,我在输入后者时出错的可能性大大降低。我在脑海中一次用文字思考,所以我发现输入“nIndex”之类的东西比输入“nIdx”之类的首字母缩写词更快,因为我实际上必须在脑海中拼出字母而不是在脑海中说出它并让我的手指自动- 输入单词(比如骑自行车)
(我的 TypingTest.com 基准 = 136 WPM)
【讨论】:
我相信 OP 指的是运行速度,而不是打字速度。 我知道。也只需要覆盖那个基础。 s/个人喜好/个人习惯/ @SamB:评论支持 s///。 xD 是的,虽然我建议这可能是由于运行时速度,但我不知道实际答案。如果是这样,我会很高兴的。 运行time head -10 >/dev/null
,每次输入10次,测量结果。我打赌你在for(;;)
会更快,除非你作弊。我大约是 14%。【参考方案7】:
在生成的机器代码方面没有区别。
然而,为了逆势而上,我认为 while(TRUE) 形式比 for(;;) 更具可读性和直观性,并且可读性和清晰性是编码指南的重要原因。我听说过 for(;;) 方法的原因(我更喜欢将我的编码指南建立在可靠的推理和/或自己的有效性证明上)。
【讨论】:
这取决于您的语言。for(;;)
是在 C 和 C++ 中执行此操作的传统方式。 while(true)
似乎是 C# 和 Java 等语言中的约定。您的里程可能会有所不同。
是的,不太熟悉 C 或 C++ 的人可能会发现 STRING_SCAN_FORMATTED 比 sscanf 更具可读性,但你看不到有人将 #define STRING_SCAN_FORMATTED sscanf 放在他们代码的顶部。跨度>
我想说的是,惯用的做法最好对您的开发人员来说是最易读的。否则你真的需要更好的开发人员。
@despart:如果有人对 C++ 不够熟悉,无法识别这个习惯用法,那么他们需要远离我的代码库。
我同意惯用的方式应该是最易读的。这就是为什么随着语言和实践的发展,总是需要挑战和改变惯用方式的原因。可怕的 K&R “惯用”编码风格花了数年时间才被现代、更易读的风格所取代,但它仍然发生了。【参考方案8】:
我看到有些人喜欢它,因为他们在这样的地方有一个#define:
#define EVER ;;
这让他们可以这样写:
for (EVER)
/* blah */
【讨论】:
我也见过;但这不是喜欢它的好理由。作为维护者,您仍然需要检查宏定义以确保它符合您的预期,而原始代码足够清晰。 RTOS VxWorks 有一个宏FOREVER
定义for(;;)
,但这也好不到哪里去。不应使用 IMO 宏来“发明”一种新语言。
实际上在某些语言中宏的目的是发明新的语法。但不管 C/C++ 中的“for (EVER)”是多么令人发指。【参考方案9】:
我无法想象一个有价值的编译器会生成任何不同的代码。即使是这样,如果不测试更有效的特定编译器,也无法确定。
但我建议你更喜欢for(;;)
,原因如下:
我使用过的一些编译器会通过适当的警告级别设置为 while(true) 生成一个常量表达式警告。
在您的示例中,宏 TRUE 可能未按您的预期定义
无限while循环有许多可能的变体,例如while(1)
、while(true)
、while(1==1)
等;所以for(;;)
可能会导致更高的一致性。
【讨论】:
TRUE 可能不是#define TRUE 1,但while(TRUE)
评估为while(0)
的任何代码库都不太可能从for(;;)
中受益。
@Justin :我同意,这确实是反常的,而且它不是三个论点中最强的,但 Bjarne Stoustrup 使用类似的论点来避免 C++ 中的 NULL 宏。在任何情况下,while(true)
都应该是首选。在 C++ 中 bool
是保留的,在 C 中定义在 C stdbool.h 中(使其在 C 中的安全性降低)。 for(;;)
消除所有疑虑。【参考方案10】:
我更喜欢for(;;)
有两个原因。
一个是一些编译器在while(true)
上产生警告(类似于“循环条件是恒定的”)。避免警告总是一件好事。
另一个是我认为for(;;)
更清晰,更有说服力。
我想要一个无限循环。从字面上看,它没有条件,它不依赖任何东西。我只是希望它永远持续下去,直到我做点什么来打破它。
而对于while(true)
,好吧,与任何事情有什么关系?我对循环不感兴趣,直到真变为假,这就是这种形式的字面意思(当真为真时循环)。我只想循环。
不,绝对没有性能差异。
【讨论】:
+1 用于避免警告。但就清晰度而言,我发现在这种情况下比 for 更清晰。我想这方面只是归结为个人喜好。 是的,偏好和习惯。如果您不习惯看到for(;;)
,它显然会看起来很奇怪。但我建议尝试习惯它,因为它是一个常见的成语,你会再次遇到它。 ;)
jalf 关于它是惯用的评论确实是这里的答案。它通常用于“无限循环”只是因为它是在 C 中编写“无限循环”的常用方式。成语不一定有充分的理由 - 它只是一个自我- 加强约定。
@Steve:我不明白我为什么曾经使用for(;condition;)
。这就是 for 的 while 循环。但就像我说的,在无限循环中,我真的不希望编译器比较 any 条件。天真地,使用while
循环,它必须在每次迭代时测试true
(显然,即使是最愚蠢的编译器也不会这样做,但这是困扰我的原则。它让我觉得你的代码过度指定了),而使用for(;;)
,您实际上是在说“没有条件可以测试。只是循环”。我发现它最接近你想象的loop ...
如果while(true)
发出警告,你的编译器就很糟糕。【参考方案11】:
我个人更喜欢for (;;)
成语(它将编译成与while (TRUE)
相同的代码。
使用while (TRUE)
可能在某种意义上更具可读性,我决定使用for (;;)
成语,因为它很突出。
在代码中应该很容易注意到或调用无限循环结构,我个人认为for (;;)
风格比while (TRUE)
或while (1)
做得更好。
另外,我记得当 while 循环的控制表达式是常量时,一些编译器会发出警告。我不认为这种情况发生得太多,但仅仅是潜在的虚假警告就足以让我想要避免它。
【讨论】:
【参考方案12】:怎么样(如果你的语言支持的话):
start:
/* BLAH */
goto start;
【讨论】:
...应该在任何合理的编译器手中生成相同的输出。 +1 !它并没有改善goto
的众多缺点,但是,如果您尝试实现的算法来自流程图,那么这是最直接的翻译。
我个人对 goto 持功利主义的观点。没有猛禽。猛禽是人。是人们开始使用 goto 来制作意大利面条代码。此外,循环的组装输出看起来很像这样。计算机的指令集没有“for”或“while”的概念,只有“jump”。
永远不要使用goto
goto
的真正好处是您不必担心有人会添加break
以使您的循环不那么无限。 (除非你已经在另一个 while
或 for
循环中...)【参考方案13】:
因为丹尼斯·里奇
我开始使用 for (;;)
是因为 Dennis Ritchie 在 K&R 中就是这样做的,而且在学习一门新语言时,我总是试图模仿聪明人。
这是惯用的 C/C++。如果您打算在 C/C++ 领域做很多事情,从长远来看,习惯它可能会更好。
你的#define
不起作用,因为被#define 的东西必须看起来像一个C 标识符。
所有现代编译器都会为这两种结构生成相同的代码。
【讨论】:
是的,这就是你知道有人从 K&R 学习 C 的方式,应该学习它的方式。每当我看到while(TRUE)
时,我都怀疑这个程序员是一个 C 新手,或者是从 For Dummies 书中学习 C 的。
我听说那些新手也用newfangled function definition style。
IIRC,在 Ritchie 的原始 C 编译器下,for(;;)
生成的代码确实比 while(1)
少,因此是首选(因为他们非常关心这些代码的大小天)。【参考方案14】:
所有好的答案 - 行为应该完全相同。
但是 - 只是假设它确实有所作为。假设其中一个每次迭代需要多 3 条指令。
你应该关心吗?
只有当你在循环中所做的事情几乎没有,几乎从来没有是这种情况。
我的观点是,有微观优化和宏观优化。微优化就像“理发减肥”。
【讨论】:
您多久看到有人被告知更喜欢++i
而不是i++
?
@Dennis Zickefoose:++i
和 i++
之间的差异可能会很大,如果 i
是类的实例而不是内置的,并且编译器不应用琐碎的优化。建议是养成一个习惯,这样它就不会在重要的时候抓住你。
++i
与 i++
的区别在于,进行“优化”不需要任何成本。诚然,在大多数情况下它完全没有区别,但它们同样易于输入,所以为什么不选择默认情况下可能最有效的呢?我想这同样适用于这里。如果一个比另一个快一点,为什么不选择更快的呢?我们这样做会失去什么?两者都很容易阅读和输入。
@Dennis Zickefoose:你是对的。我在 SO 上看到了很多,Clifford 的评论是正确的。此外,我在 SO 上没有看到很多东西,那就是如何进行宏观优化,例如 43 倍加速:***.com/questions/926266/…。所以我想知道我们的优先事项。
@Mike:是的,但这是一个苹果与橘子的比较。理发需要实际的时间,甚至可能需要金钱。我同意这是一个微优化,它并不重要。但它也不会花费我们任何东西。如果我可以选择向左或向右,并且距离相同,地形相同,一切都相同,除了左边的路径有一些边际微观利益,为什么不向左走?你是对的,这不是一个令人印象深刻的论点。但是当成本实际上为零时,我认为没有理由更喜欢不太理想的路线。【参考方案15】:
while(true)
使用 Visual Studio 生成警告(条件不变)。我工作过的大多数地方都会编译带有警告作为错误的生产版本。所以
for(;;)
是首选。
【讨论】:
您使用的是什么版本的 Visual Studio?在 2008 年,我没有收到此警告,即使是警告级别 4。【参考方案16】:我认为 while(true) 比 for(;;) 更具可读性 -- 看起来程序员在 for 循环中遗漏了一些东西 :)
【讨论】:
我关心速度。特别是如果有一个优化会无限次影响我的代码。【参考方案17】:我更喜欢for (;;)
,因为它在不同的类 C 语言中是最一致的。
在 C++ 中 while (true)
很好,但在 C 中你需要一个头文件来定义 true
,而 TRUE
也是一个常用的宏。如果您使用while (1)
,它在 C 和 C++ 以及 javascript 中是正确的,但在 Java 或 C# 中是正确的,它们要求循环条件是布尔值,例如 while (true)
或 while (1 == 1)
。在 php 中,关键字不区分大小写,但该语言更喜欢大写 TRUE
。
但是,for (;;)
在所有这些语言中总是完全正确的。
【讨论】:
感谢您的回答!我认为这实际上是第一个显示for (;;)
更好的客观方式的答案。
我认为我不需要用 C、C++、JavaScript、Java 和 C# 编写正确的代码。它们是不同的语言。
@KeithThompson 这不是要编写在多种语言中都正确的代码。我认为这是为了避免在不同背景的开发者之间产生误解。
@sam:while (1)
和for (;;)
都是 C 中无限循环的常见习语。任何阅读 C 程序的人如果无法理解其中的任何一个,都可能无法理解其余代码。不知道 C 的人可以轻松阅读的 C 代码是不值得的。
@KeithThompson 虽然一个称职的 C 读者肯定应该熟悉这两个习语,但如果作者是一位多语种程序员,这也很有用。在我的上一份工作中,我每天都在使用 JS、ActionScript 和 PHP,并且统一编写代码的方式(这样做是有意义的)可以使编程更快,维护更容易。【参考方案18】:
for(;;Sleep(50))
// Lots of code
比更清楚:
while(true)
// Lots of code
Sleep(50);
如果您不使用Sleep()
,则不适用。
【讨论】:
在这样的 for 循环中调用 sleep 之类的函数并不干净 这不是等价的。当循环中有continue
时,for
循环休眠,但while
循环不休眠。【参考方案19】:
使用“for(;;)”的最重要原因是害怕在进行探索性编程时使用“while(TRUE)”。使用“for”更容易控制重复次数,也更容易将“for”循环转换为无限循环。
例如,如果您正在构造一个递归函数,您可以在转换为无限循环之前限制对该函数的调用次数。
for(int i=0;i<1000;i++) recursiveFunction(oneParam);
当我确定我的功能时,我将其转换为无限循环:
for(;;) recursiveFunction(oneParam);
【讨论】:
【参考方案20】:如果您的代码经过编译器优化,两者应该相同。为了解释我所说的优化是什么意思,这里有一个用 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)
对于一个开启速度优化的体面编译器来说,无论您使用哪个都无关紧要。
【讨论】:
这其实是一个非常好的点!如果没有优化,即使是现代编译器也会有几条指令不同。【参考方案21】:正如其他人所指出的,从技术角度来看,这根本不重要。有些人认为一个比另一个更具可读性,他们对它是什么有不同的看法。
对我来说,这基本上只是吹毛求疵,因为任何 C 程序员都应该能够立即将while(1)
和for(;;)
识别为无限循环。
您的定义将不起作用。但是,您可以(但不应该)这样做:
#define repeat for(;;)
int main(void)
repeat
puts("Hello, World");
但真的,不要做那样的事情......
【讨论】:
以上是关于“for(;;)”比“while (true)”快吗?如果不是,人们为啥要使用它?的主要内容,如果未能解决你的问题,请参考以下文章
关于死循环while(true){}或for(;;){}的总结