C/C++ 中的无限循环

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20186809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:58:05  来源:igfitidea点击:

Endless loop in C/C++

c++cloopsinfinite-loop

提问by magu_

There are several possibilities to do an endless loop, here are a few I would choose:

无限循环有几种可能,我会选择以下几种:

  • for(;;) {}
  • while(1) {}/ while(true) {}
  • do {} while(1)/ do {} while(true)
  • for(;;) {}
  • while(1) {}/ while(true) {}
  • do {} while(1)/ do {} while(true)

Is there a certain form which one should choose? And do modern compilers make a difference between the middle and the last statement or does it realize that it is an endless loop and skips the checking part entirely?

是否有某种形式应该选择哪一种?现代编译器是否对中间语句和最后一条语句有所不同,或者它是否意识到这是一个无限循环并完全跳过检查部分?

Edit: as it has been mentioned I forgot goto, but this was done out of the reason that I don't like it as a command at all.

编辑:正如我所提到的,我忘记了goto,但这样做的原因是我根本不喜欢它作为命令。

Edit2: I made some grep on the latest versions taken from kernel.org. I does seems as nothing much changed over time (within the Kernel at least) enter image description here

Edit2:我对从 kernel.org 获取的最新版本做了一些 grep。随着时间的推移,我似乎没有太大变化(至少在内核中) 在此处输入图片说明

回答by Lundin

The problem with asking this question is that you'll get so many subjective answers that simply state "I prefer this...". Instead of making such pointless statements, I'll try to answer this question with facts and references, rather than personal opinions.

问这个问题的问题是你会得到很多主观的答案,只是简单地说明“我更喜欢这个……”。我将尝试用事实和参考而非个人意见来回答这个问题,而不是做出这种毫无意义的陈述。

Through experience, we can probably start by excluding the do-while alternatives (and the goto), as they are not commonly used. I can't recall ever seeing them in live production code, written by professionals.

根据经验,我们可能可以从排除 do-while 替代方案(和 goto)开始,因为它们不常用。我不记得在由专业人士编写的实时生产代码中看到过它们。

The while(1), while(true)and for(;;)are the 3 different versions commonly existing in real code. They are of course completely equivalent and results in the same machine code.

while(1)while(true)并且for(;;)是3个不同的版本,在实际的代码普遍存在。它们当然是完全等效的,并产生相同的机器代码。



for(;;)

for(;;)

  • This is the original, canonical example of an eternal loop. In the ancient C bible The C Programming Languageby Kernighan and Ritchie, we can read that:

    K&R 2nd ed 3.5:

    for (;;) {
    ...
    }
    

    is an "infinite" loop, presumably to be broken by other means, such as a break or return. Whether to use while or for is largely a matter of personal preference.

    For a long while (but not forever), this book was regarded as canon and the very definition of the C language. Since K&R decided to show an example of for(;;), this would have been regarded as the most correct form at least up until the C standardization in 1990.

    However, K&R themselves already stated that it was a matter of preference.

    And today, K&R is a very questionable source to use as a canonical C reference. Not only is it outdated several times over (not addressing C99 nor C11), it also preaches programming practices that are often regarded as bad or blatantly dangerous in modern C programming.

    But despite K&R being a questionable source, this historical aspect seems to be the strongest argument in favour of the for(;;).

  • The argument against the for(;;)loop is that it is somewhat obscure and unreadable. To understand what the code does, you must know the following rule from the standard:

    ISO 9899:2011 6.8.5.3:

    for ( clause-1 ; expression-2 ; expression-3 ) statement
    

    /--/

    Both clause-1 and expression-3 can be omitted. An omitted expression-2 is replaced by a nonzero constant.

    Based on this text from the standard, I think most will agree that it is not only obscure, it is subtle as well, since the 1st and 3rd part of the for loop are treated differently than the 2nd, when omitted.

  • 这是永恒循环的原始规范示例。在Kernighan 和 Ritchie的古老 C 圣经The C Programming Language 中,我们可以读到:

    K&R 第二版 3.5:

    for (;;) {
    ...
    }
    

    是一个“无限”循环,大概会被其他方式打破,例如中断或返回。使用 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
    

    /--/

    子句 1 和表达式 3 都可以省略。省略的表达式 2 由非零常量替换。

    根据标准中的这段文字,我认为大多数人会同意它不仅晦涩难懂,而且很微妙,因为 for 循环的第 1 部分和第 3 部分在省略时的处理方式与第 2 部分不同。



while(1)

while(1)

  • This is supposedly a more readable form than for(;;). However, it relies on another obscure, although well-known rule, namely that C treats all non-zero expressions as boolean logical true. Every C programmer is aware of that, so it is not likely a big issue.

  • There is one big, practical problem with this form, namely that compilers tend to give a warning for it: "condition is always true" or similar. That is a good warning, of a kind which you really don't want to disable, because it is useful for finding various bugs. For example a bug such as while(i = 1), when the programmer intended to write while(i == 1).

    Also, external static code analysers are likely to whine about "condition is always true".

  • 据说这是一种比for(;;). 然而,它依赖于另一个晦涩但众所周知的规则,即 C 将所有非零表达式视为布尔逻辑真。每个 C 程序员都知道这一点,所以这不是什么大问题。

  • 这种形式存在一个大的实际问题,即编译器倾向于为它发出警告:“条件总是为真”或类似的。这是一个很好的警告,您真的不想禁用它,因为它对于查找各种错误很有用。例如while(i = 1),当程序员打算编写一个 bug时while(i == 1)

    此外,外部静态代码分析器可能会抱怨“条件始终为真”。



while(true)

while(true)

  • To make while(1)even more readable, some use while(true)instead. The consensus among programmers seem to be that this is the most readable form.

  • However, this form has the same problem as while(1), as described above: "condition is always true" warnings.

  • When it comes to C, this form has another disadvantage, namely that it uses the macro truefrom stdbool.h. So in order to make this compile, we need to include a header file, which may or may not be inconvenient. In C++ this isn't an issue, since boolexists as a primitive data type and trueis a language keyword.

  • Yet another disadvantage of this form is that it uses the C99 bool type, which is only available on modern compilers and not backwards compatible. Again, this is only an issue in C and not in C++.

  • 为了while(1)提高可读性,有些人while(true)改为使用。程序员之间的共识似乎是这是最易读的形式。

  • 但是,此表单与 存在相同的问题while(1),如上所述:“条件始终为真”警告。

  • 对于 C,这种形式还有另一个缺点,即它使用了truestdbool.h 中的宏。所以为了进行这个编译,我们需要包含一个头文件,这可能不方便。在 C++ 中,这不是问题,因为bool作为原始数据类型存在并且true是语言关键字。

  • 这种形式的另一个缺点是它使用 C99 bool 类型,该类型仅在现代编译器上可用且不向后兼容。同样,这只是 C 中的问题,而不是 C++ 中的问题。



So which form to use? Neither seems perfect. It is, as K&R already said back in the dark ages, a matter of personal preference.

那么使用哪种形式呢?两者似乎都不完美。正如 K&R 在黑暗时代已经说过的那样,这是个人喜好的问题。

Personally, I always use for(;;)just to avoid the compiler/analyser warnings frequently generated by the other forms. But perhaps more importantly because of this:

就个人而言,我总是使用for(;;)just 来避免其他形式经常生成的编译器/分析器警告。但也许更重要的原因是:

If even a C beginner knowsthat for(;;)means an eternal loop, then who are you trying to make the code more readable for?

如果即使是 C 初学者也知道for(;;)意味着一个永恒的循环,那么您想让谁的代码更具可读性?

I guess that's what it all really boils down to. If you find yourself trying to make your source code readable for non-programmers, who don't even know the fundamental parts of the programming language, then you are only wasting time. They should not be reading your code.

我想这就是它真正归结起来的原因。如果您发现自己试图让您的源代码对那些甚至不了解编程语言的基本部分的非程序员可读,那么您只是在浪费时间。他们不应该阅读您的代码。

And since everyone who shouldbe reading your code already knows what for(;;)means, there is no point in making it further readable - it is already as readable as it gets.

而且由于每个应该阅读您的代码的人都已经知道其for(;;)含义,因此没有必要进一步提高它的可读性——它已经尽可能具有可读性。

回答by Nawaz

It is very subjective. I write this:

这是非常主观的。我这样写:

while(true) {} //in C++

Because its intent is very much clearand it is also readable: you look at it and you knowinfinite loop is intended.

因为它的意图非常明确而且可读:你看它就会知道无限循环的。

One might say for(;;)is also clear. But I would argue that because of its convoluted syntax, this option requiresextra knowledge to reach the conclusion that it is an infinite loop, hence it is relatively lessclear. I would even say there are morenumber of programmers who don't know what for(;;)does (even if they know usual forloop), but almostall programmers who knows whileloop would immediately figure out what while(true)does.

可以说for(;;)也很清楚。但我认为,由于其复杂的语法,此选项需要额外的知识才能得出它是无限循环的结论,因此相对不太清楚。我什至会说有更多的程序员不知道什么for(;;)是(即使他们知道通常的for循环),但几乎所有知道while循环的程序员都会立即弄清楚是什么while(true)

To me, writing for(;;)to mean infinite loop, is like writing while()to mean infinite loop — while the former works, the latter does NOT. In the former case, emptycondition turns out to be trueimplicitly, but in the latter case, it is an error! I personallydidn't like it.

对我来说,写作for(;;)意味着无限循环,就像写作while()意味着无限循环——虽然前者有效,后者不起作用。在前一种情况下,条件原来是true隐式的,但在后一种情况下,它是一个错误!我个人不喜欢它。

Now while(1)is also there in the competition. I would ask: why while(1)? Why not while(2), while(3)or while(0.1)? Well, whatever you write, you actually meanwhile(true)— if so, then why not write it instead?

现在while(1)也在比赛中。我会问:为什么while(1)?为什么不while(2)while(3)或者while(0.1)?好吧,不管你写什么,你的意思是while(true)——如果是这样,那为什么不写呢?

In C (if I ever write), I would probably write this:

在 C 中(如果我曾经写过),我可能会这样写:

while(1) {} //in C

While while(2), while(3)and while(0.1)would equally make sense. But just to be conformant with other C programmers, I would write while(1), because lots of C programmers write this and I find no reason to deviate from the norm.

While while(2), while(3)andwhile(0.1)同样有意义。但是为了与其他 C 程序员保持一致,我会写while(1),因为很多 C 程序员都写了这个,我觉得没有理由偏离规范。

回答by Pita

In an ultimate act of boredom, I actually wrote a few versions of these loops and compiled it with GCC on my mac mini.

在最后的无聊行为中,我实际上编写了这些循环的几个版本,并在我的 mac mini 上用 GCC 编译它。

the while(1){}and for(;;) {}produced same assembly results while the do{} while(1);produced similar but a different assembly code

while(1){}for(;;) {}而产生的相同结果组件do{} while(1);生产的类似但是具有不同的汇编代码

heres the one for while/for loop

继承人 for 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    
        .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    
if (condition == true) ...
, -4(%rbp) LBB0_1: ## =>This Inner Loop Header: Depth=1 jmp LBB0_2 LBB0_2: ## in Loop: Header=BB0_1 Depth=1 movb , %al testb , %al jne LBB0_1 jmp LBB0_3 LBB0_3: movl
if (condition) ...
, %eax popq %rbp ret .cfi_endproc
, -4(%rbp) LBB0_1: ## =>This Inner Loop Header: Depth=1 jmp LBB0_1 .cfi_endproc

and the do while loop

和 do while 循环

cat /etc/*-release
CentOS release 6.4 (Final)

回答by Kaz

The idiom designed into the C language (and inherited into C++) for infinite looping is for(;;): the omission of a test form. The do/whileand whileloops do not have this special feature; their test expressions are mandatory.

为无限循环设计到 C 语言中(并继承到 C++ 中)的习语是for(;;):省略测试表单。在do/whilewhile循环没有这种特殊的功能; 他们的测试表达式是强制性的。

for(;;)does not express "loop while some condition is true that happens to always be true". It expresses "loop endlessly". No superfluous condition is present.

for(;;)不表示“循环,而某些条件为真但碰巧总是为真”。它表示“无限循环”。不存在多余的条件。

Therefore, the for(;;)construct is the canonicalendless loop. This is a fact.

因此,for(;;)构造是规范的无限循环。这是事实。

All that is left to opinion is whether or not to write the canonical endless loop, or to choose something baroque which involves extra identifiers and constants, to build a superfluous expression.

剩下的就是是否编写规范的无限循环,或者选择一些涉及额外标识符和常量的巴洛克风格来构建一个多余的表达式。

Even if the test expression of whilewere optional, which it isn't, while();would be strange. whilewhat? By contrast, the answer to the question forwhat? is: why, ever---for ever! As a joke some programmers of days past have defined blank macros, so they could write for(ev;e;r);.

即使 的测试表达式while是可选的,但它不是,while();也会很奇怪。while什么?相比之下,问题的答案是for什么?是:为什么,永远——永远!开个玩笑,过去的一些程序员定义了空白宏,因此他们可以编写for(ev;e;r);.

while(true)is superior to while(1)because at least it doesn't involve the kludge that 1 represents truth. However, while(true)didn't enter into C until C99. for(;;)exists in every version of C going back to the language described in the 1978 book K&R1, and in every dialect of C++, and even related languages. If you're coding in a code base written in C90, you have to define your own truefor while (true).

while(true)优于while(1)因为至少它不涉及 1 代表真理的混乱。但是,while(true)直到C99才进入C。for(;;)存在于 C 的每个版本中,可以追溯到 1978 年出版的 K&R1 一书中描述的语言,以及 C++ 的每个方言,甚至相关语言中。如果您使用 C90 编写的代码库进行编码,则必须truewhile (true).

while(true)reads badly. While whatis true? We don't really want to see the identifier truein code, except when we are initializing boolean variables or assigning to them. trueneed not ever appear in conditional tests. Good coding style avoids cruft like this:

while(true)读不好。而什么是真的?我们真的不想true在代码中看到标识符,除非我们初始化布尔变量或给它们赋值。true不需要出现在条件测试中。良好的编码风格可以避免像这样的杂乱:

Target: x86_64-unknown-linux-gnu
Thread model: posix
gcc version 4.8.2 (GCC)

in favor of:

有利于:

// 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; }
}

For this reason while (0 == 0)is superior to while (true): it uses an actual condition that tests something, which turns into a sentence: "loop while zero is equal to zero." We need a predicateto go nicely with "while"; the word "true" isn't a predicate, but the relational operator ==is.

由于这个原因while (0 == 0)优于while (true):它使用一个实际条件来测试某些东西,它变成了一个句子:“循环而零等于零”。我们需要一个谓词来与“while”搭配得很好;“真”这个词不是谓词,但关系运算符==是。

回答by Kaz

Everyone seems to like while (true):

每个人似乎都喜欢while (true)

https://stackoverflow.com/a/224142/1508519

https://stackoverflow.com/a/224142/1508519

https://stackoverflow.com/a/1401169/1508519

https://stackoverflow.com/a/1401169/1508519

https://stackoverflow.com/a/1401165/1508519

https://stackoverflow.com/a/1401165/1508519

https://stackoverflow.com/a/1401164/1508519

https://stackoverflow.com/a/1401164/1508519

https://stackoverflow.com/a/1401176/1508519

https://stackoverflow.com/a/1401176/1508519

According to SLaks, they compile identically.

根据SLaks,它们的编译方式相同。

Ben Zotto also says it doesn't matter:

Ben Zotto 也说没关系

It's not faster. If you really care, compile with assembler output for your platform and look to see. It doesn't matter. This never matters. Write your infinite loops however you like.

它不是更快。如果您真的很关心,请为您的平台使用汇编器输出进行编译并查看。没关系。这从来都不重要。随心所欲地编写无限循环。

In response to user1216838, here's my attempt to reproduce his results.

响应 user1216838,这是我尝试重现他的结果。

Here's my machine:

这是我的机器:

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

gcc version:

gcc 版本:

test1.asm test2.asm differ: byte 16, line 1

And test files:

和测试文件:

        .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    ##代码##, %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    , %rsp
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        cmpl    , -4(%rbp)
        jne     .L3
        cmpl    535, -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    535, %esi
        movl    , %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

The commands:

命令:

##代码##

The only difference is the first line, aka the filename.

唯一的区别是第一行,也就是文件名。

##代码##

Output:

输出:

##代码##

With -O3, the output is considerably smaller of course, but still no difference.

使用-O3,输出当然要小得多,但仍然没有区别。

回答by Chris Chambers

They probably compile down to nearly the same machine code, so it is a matter of taste.

它们可能编译成几乎相同的机器代码,所以这是一个品味问题。

Personally, I would chose the one that is the clearest (i.e. very clear that it is supposed to be an infinite loop).

就个人而言,我会选择最清晰的一个(即非常清楚它应该是一个无限循环)。

I would lean towards while(true){}.

我会倾向于while(true){}

回答by Aerom Xundes

I use for(;/*ever*/;).

我用for(;/*ever*/;).

It is easy to read and it takes a bit longer to type (due to the shifts for the asterisks), indicating I should be really careful when using this type of loop. The green text that shows up in the conditional is also a pretty odd sight—another indication this construct is frowned upon unless absolutely necessary.

它很容易阅读,而且打字需要更长的时间(由于星号的变化),这表明我在使用这种类型的循环时应该非常小心。出现在条件句中的绿色文本也是一个非常奇怪的景象——除非绝对必要,否则这个结构是不受欢迎的另一个迹象。

回答by Thomas Padron-McCarthy

I would recommend while (1) { }or while (true) { }. It's what most programmers would write, and for readability reasons you should follow the common idioms.

我会推荐while (1) { }while (true) { }。这是大多数程序员会写的内容,出于可读性原因,您应该遵循常见的习惯用法。

(Ok, so there is an obvious "citation needed" for the claim about most programmers. But from the code I've seen, in C since 1984, I believe it is true.)

(好吧,对于大多数程序员的说法,显然需要“引用”。但从我所看到的代码来看,自 1984 年以来在 C 中,我相信这是真的。)

Any reasonable compiler would compile all of them to the same code, with an unconditional jump, but I wouldn't be surprised if there are some unreasonablecompilers out there, for embedded or other specialized systems.

任何合理的编译器都会将它们全部编译为相同的代码,并进行无条件跳转,但如果有一些不合理的编译器用于嵌入式或其他专用系统,我不会感到惊讶。

回答by haccks

Is there a certain form which one should choose?

是否有某种形式应该选择哪一种?

You can choose either. Its matter of choice. All are equivalent. while(1) {}/while(true){}is frequently used for infinite loop by programmers.

您可以选择两者之一。它的选择问题。都是等价的。while(1) {}/while(true){}经常被程序员用于无限循环。

回答by RichardPlunkett

Well, there is a lot of taste in this one. I think people from a C background are more likely to prefer for(;;), which reads as "forever". If its for work, do what the locals do, if its for yourself, do the one that you can most easily read.

嗯,这个有很多味道。我认为来自 C 背景的人更有可能更喜欢 for(;;),它读作“永远”。如果是为了工作,就做当地人做的事情,如果是为了自己,做你最容易阅读的事情。

But in my experience, do { } while (1); is almost never used.

但根据我的经验,执行 { } while (1); 几乎从未使用过。