最近我发现了一篇文章,该文章声称在无限循环中选择for(;;)
而不是while(1)
的想法是因为PDP-11上最初可用的C编译器为while(1)
生成了额外的机器指令。
顺便说一句,现在甚至Visual c++的警告也倾向于前者。
对for(;;)
成语的这种归因有多现实?
在最初的K&R中明确提到了"for(;;)"习语。这对我来说就足够了
谨防某些"感性归因",因为往往是由于缺乏上下文而导致的假消息来源。OP将消息标记为C和c++。
现在,K&R可以是C历史的半神,但肯定不能被认为是c++的权威。此外,编译器优化可以在c++中发挥基本作用,但C系统程序员经常认为这是"滥用"(他们喜欢将C视为"带有表达式的汇编程序",而不是"高级语言")。
现在的编译器很可能会生成完全相同的代码(这很容易证明),使用其中一个或另一个更多的是品味问题,而不是其他。
在这个意义上,我倾向于for(;;)
,因为我可以很容易地把它读成"for-ever",而while(true)
读成"而这个真实的东西是真实的",让你弄清楚如果它甚至可能是假的... .浪费了2毫秒的大脑!(但这是我的个人意见:我知道很多人不得不考虑for(;;)
而不是while(true)
)
然而,我也能认出它们都是"图像表示"(不实际阅读文本,只是通过摄影记忆看看它们是什么样子),指向同一个智力概念(待在这里直到有人把你从里面踢出去)。
关于MS警告,有时它可以避免您编写错误的表达式(如true||a
)。但是显然被滥用了,不应该出现在里面没有操作的琐碎表达式中。在这两种情况下,MS编译器都会生成相同的机器码。也许给微软的反馈会让他们在未来的版本中对这个警告不那么乏味。
以下是V7 Unix编译器cc
生成的结果(使用SIMH和来自TUHS的图像):
$ cat>a.c
main(){
while(1);
}
$ cat>b.c
main(){
for(;;);
}
$ cc -S a.c
$ cc -S b.c
a.c
(while
)编译为:
.globl _main
.text
_main:
~~main:
jsr r5,csv
jbr L1
L2:L4:tst $1
jeq L5
jbr L4
L5:L3:jmp cret
L1:jbr L2
.globl
.data
当b.c
(for
)变成:
.globl _main
.text
_main:
~~main:
jsr r5,csv
jbr L1
L2:L4:jbr L4
L5:L3:jmp cret
L1:jbr L2
.globl
.data
所以for(;;)
在不使用优化的情况下编译到更少的指令是正确的。然而,当使用-O
编译时,两个程序产生的完全是相同的程序集:
.globl _main
.text
_main:
~~main:
jsr r5,csv
L4:jbr L4
.globl
.data
,当我添加一个printf("Hello");
的循环体时,程序仍然是一样的。
所以,这个习语可能起源于PDP-11机器语言,但到1979年,这种差异已经在很大程度上无关重要了。
我不知道这是不是真的,但是文章的说法是明智的和现实的。for(;;)
比while(1)
更短
我想你会发现K&R并没有编写PDP-11编译器,因为PDP-11是一个"机器"(而不是一种语言),它已经为自己的PDP-11指令集拥有了自己的MACRO汇编器。
K&R{据说)写了他们自己的'C'编译器…嗯. .如果它必须是宏,不是吗(因为这是PDP上唯一可用的指令集汇编器),然后(据说)用'C'编写Unix,因为他们认为他们可以比DEC的PDP-11操作系统(RT-11和RSTS-11)做得更好。他们可能是对的!
for(;;)
是惯用的,所以如果你的代码只有经验丰富的C程序员才能阅读,那就太好了。while(true)
对于没有经验的程序员和非C程序员来说更容易理解。前者还依赖于一些不明显的行为,即空布尔表达式的计算结果为true。基于这些原因,如果你必须选择一个,我会说选择后者。
然而我认为在几乎所有的实际情况下,程序员都不希望代码永远循环。希望循环终止总是有原因的,即使只是用户点击了control-C。不需要无限循环