mag*_*gu_ 57 c c++ loops infinite-loop
有几种可能性来进行无限循环,这里有一些我会选择:
for(;;) {}while(1) {}/while(true) {}do {} while(1)/do {} while(true)是否有某种形式应该选择?现代编译器是否会在中间和最后一个语句之间产生影响,或者它是否意识到它是一个无限循环并完全跳过检查部分?
编辑:因为已经提到我忘记了goto,但这是因为我根本不喜欢它作为一个命令.
Edit2:我对从kernel.org获取的最新版本做了一些grep.我确实看起来随着时间的推移没有太大变化(至少在内核中)

Lun*_*din 85
提出这个问题的问题在于,你会得到如此多的主观答案,简单地说"我更喜欢这个......".我不会做出这样毫无意义的陈述,而是试图用事实和参考来回答这个问题,而不是个人意见.
通过经验,我们可以从排除do-while替代品(和goto)开始,因为它们不常用.我记不起在专业人士撰写的现场制作代码中看到它们了.
这是while(1),while(true)并且for(;;)是真实代码中常见的3种不同版本.它们当然完全等效,并产生相同的机器代码.
for(;;)
这是永恒循环的原始,规范的例子.在Kernighan和Ritchie 的古老的C语言编程语言中,我们可以读到:
K&R第二版3.5:
for (;;) {
...
}
Run Code Online (Sandbox Code Playgroud)
是一个"无限"的循环,可能被其他方式打破,例如休息或回归.是否使用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 ) statement
Run Code Online (Sandbox Code Playgroud)
/ - /
可以省略子句-1和表达式3.省略的表达式-2由非零常量替换.
基于标准中的这个文本,我认为大多数人都同意它不仅模糊不清,而且也很微妙,因为for循环的第1和第3部分与第2部分的处理方式不同,省略时.
while(1)
据说这比形式更易读for(;;).然而,它依赖于另一个模糊的,虽然众所周知的规则,即C将所有非零表达式视为布尔逻辑真.每个C程序员都知道这一点,因此不太可能是一个大问题.
这种形式存在一个重大的实际问题,即编译器倾向于给出警告:"条件总是正确的"或类似的.这是一个很好的警告,你真的不想禁用它,因为它有助于发现各种错误.例如while(i = 1),当程序员打算写的时候出现的错误while(i == 1).
此外,外部静态代码分析器可能会抱怨"条件总是如此".
while(true)
为了使while(1)更具可读性,一些人使用while(true).程序员之间的共识似乎是这是最易读的形式.
但是,while(1)如上所述,这种形式具有相同的问题:"条件始终为真"警告.
说到C,这个表单有另一个缺点,即它使用truestdbool.h中的宏.因此,为了进行编译,我们需要包含一个头文件,这可能会或可能不会很不方便.在C++中,这不是问题,因为它bool作为原始数据类型存在并且true是语言关键字.
这种形式的另一个缺点是它使用C99 bool类型,它只能在现代编译器上使用,而不能向后兼容.同样,这只是C中的问题,而不是C++中的问题.
那么使用哪种形式?似乎都不完美.正如K&R已经在黑暗时代所说的那样,这是个人偏好的问题.
就个人而言,我总是使用for(;;)它来避免其他表单经常生成的编译器/分析器警告.但也许更重要的是因为:
如果即使是C初学者都知道这for(;;)意味着一个永恒的循环,那么你是谁试图使代码更具可读性?
我猜这就是它真正归结为的.如果你发现自己试图使你的源代码对非程序员来说是可读的,那些人甚至不知道编程语言的基本部分,那么你只是在浪费时间.他们不应该阅读你的代码.
因为每个应该 阅读你的代码的人都已经知道了什么for(;;)意思,所以没有必要让它更具可读性 - 它已经具备了可读性.
Naw*_*waz 32
这是非常主观的.我写这个:
while(true) {} //in C++
Run Code Online (Sandbox Code Playgroud)
因为它的意图非常明确并且它也是可读的:你看它并且你知道 无限循环.
有人可能会说for(;;)也很清楚.但我认为,由于其语法错综复杂,这个选项需要额外的知识来得出它是无限循环的结论,因此它相对不太清楚.我甚至会说有更多的程序员不知道是什么for(;;)(即使他们知道通常的for循环),但几乎所有知道while循环的程序员都会立即弄清楚是什么while(true).
对我来说,写作for(;;)意味着无限循环,就像写作while()意味着无限循环 - 而前者是有效的,后者则不是.在前一种情况下,空条件被证明是true隐含的,但在后一种情况下,它是一个错误!我个人不喜欢它.
现在while(1)也在比赛中.我会问:为什么while(1)?为什么不while(2),while(3)或while(0.1)?好吧,无论你写什么,你的意思是 while(true) - 如果是这样,那么为什么不写呢?
在C中(如果我写过),我可能会这样写:
while(1) {} //in C
Run Code Online (Sandbox Code Playgroud)
虽然while(2),while(3)并且while(0.1)同样有道理.但是为了与其他C程序员保持一致,我会写while(1),因为很多C程序员都写这个,我发现没有理由偏离常规.
Pit*_*ita 29
在一个无聊的终极行为中,我实际上写了几个版本的这些循环,并在我的mac mini上用GCC编译它.
的
while(1){}和for(;;) {}
产生的相同组件的结果而do{} while(1);产生的相似,但不同的汇编代码
继续使用while/for循环
.section __TEXT,__text,regular,pure_instructions
.globl _main
.align 4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
movl $0, -4(%rbp)
LBB0_1: ## =>This Inner Loop Header: Depth=1
jmp LBB0_1
.cfi_endproc
Run Code Online (Sandbox Code Playgroud)
和while while循环
.section __TEXT,__text,regular,pure_instructions
.globl _main
.align 4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
movl $0, -4(%rbp)
LBB0_1: ## =>This Inner Loop Header: Depth=1
jmp LBB0_2
LBB0_2: ## in Loop: Header=BB0_1 Depth=1
movb $1, %al
testb $1, %al
jne LBB0_1
jmp LBB0_3
LBB0_3:
movl $0, %eax
popq %rbp
ret
.cfi_endproc
Run Code Online (Sandbox Code Playgroud)
小智 8
每个人似乎都喜欢while (true):
根据SLaks的说法,他们编译相同.
它并不快.如果您真的关心,请使用您的平台的汇编程序输出进行编译,并查看.没关系.这永远不重要.写下你喜欢的无限循环.
作为对用户1216838的回应,这是我尝试重现他的结果.
这是我的机器:
cat /etc/*-release
CentOS release 6.4 (Final)
Run Code Online (Sandbox Code Playgroud)
gcc版本:
Target: x86_64-unknown-linux-gnu
Thread model: posix
gcc version 4.8.2 (GCC)
Run Code Online (Sandbox Code Playgroud)
和测试文件:
// testing.cpp
#include <iostream>
int main() {
do { break; } while(1);
}
// testing2.cpp
#include <iostream>
int main() {
while(1) { break; }
}
// testing3.cpp
#include <iostream>
int main() {
while(true) { break; }
}
Run Code Online (Sandbox Code Playgroud)
命令:
gcc -S -o test1.asm testing.cpp
gcc -S -o test2.asm testing2.cpp
gcc -S -o test3.asm testing3.cpp
cmp test1.asm test2.asm
Run Code Online (Sandbox Code Playgroud)
唯一的区别是第一行,即文件名.
test1.asm test2.asm differ: byte 16, line 1
Run Code Online (Sandbox Code Playgroud)
输出:
.file "testing2.cpp"
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.text
.globl main
.type main, @function
main:
.LFB969:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
nop
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE969:
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB970:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L3
cmpl $65535, -8(%rbp)
jne .L3
movl $_ZStL8__ioinit, %edi
call _ZNSt8ios_base4InitC1Ev
movl $__dso_handle, %edx
movl $_ZStL8__ioinit, %esi
movl $_ZNSt8ios_base4InitD1Ev, %edi
call __cxa_atexit
.L3:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE970:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB971:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE971:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .ctors,"aw",@progbits
.align 8
.quad _GLOBAL__sub_I_main
.hidden __dso_handle
.ident "GCC: (GNU) 4.8.2"
.section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)
随着-O3,输出当然是相当小,但仍然没有什么区别.
设计成C语言(并继承到C++)进行无限循环的习语是for(;;):省略了测试形式.在do/while和while循环没有这种特殊的功能; 他们的测试表达是强制性的
for(;;)不表达"循环,而某些条件是真的,恰好总是如此".它表达了"无休止地循环".没有多余的条件存在.
因此,for(;;)构造是规范的无限循环.这是事实.
所有留待观点的是,是否要编写规范的无限循环,或选择一些涉及额外标识符和常量的巴洛克,以构建多余的表达式.
即使测试表达式while是可选的,但它不是,while();也会很奇怪.while什么?相比之下,问题的答案是for什么?是:为什么,永远 - 永远!作为一个笑话,一些过去的程序员已经定义了空白宏,所以他们可以写for(ev;e;r);.
while(true)是优越的,while(1)因为至少它不涉及1代表真理的kludge.但是,while(true)直到C99才进入C. for(;;)存在于C的每个版本中,这些版本可以追溯到1978年出版的K&R1中所描述的语言,以及C++的每种方言,甚至是相关语言.如果你在写的C90代码库的编码,你必须定义自己true的while (true).
while(true)读得很糟糕.而什么是真实的?我们真的不想true在代码中看到标识符,除非我们初始化布尔变量或分配它们.true不必出现在条件测试中.良好的编码风格避免像这样:
if (condition == true) ...
Run Code Online (Sandbox Code Playgroud)
有利于:
if (condition) ...
Run Code Online (Sandbox Code Playgroud)
由于这个原因while (0 == 0)优于while (true):它使用测试某些东西的实际条件,变成一个句子:"循环,而零等于零." 我们需要一个谓词与"while"很好地结合; 单词"true"不是谓词,而是关系运算符==.
我用for(;/*ever*/;).
它很容易阅读,而且打字需要更长的时间(由于星号的变化),这表明我在使用这种类型的循环时应该非常小心。出现在条件句中的绿色文本也是一个非常奇怪的景象——除非绝对必要,否则这个结构是不受欢迎的另一个迹象。
| 归档时间: |
|
| 查看次数: |
94154 次 |
| 最近记录: |