development

C / C ++의 무한 루프

big-blog 2020. 12. 26. 16:31
반응형

C / C ++의 무한 루프


무한 루프를 수행 할 수있는 몇 가지 가능성이 있습니다. 다음은 몇 가지 선택 사항입니다.

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

선택해야 할 특정 형식이 있습니까? 그리고 현대의 컴파일러는 중간과 마지막 문 사이에 차이를 만들까요, 아니면 끝없는 루프이고 검사 부분을 완전히 건너 뛰는 것을 인식합니까?

편집 : 언급했듯이 잊었 goto지만 명령으로 전혀 좋아하지 않는 이유에서 수행되었습니다.

Edit2 : kernel.org에서 가져온 최신 버전에 대해 grep을 만들었습니다. 나는 시간이 지남에 따라 크게 변하지 않는 것처럼 보입니다 (적어도 커널 내에서)여기에 이미지 설명 입력


이 질문을 할 때의 문제는 단순히 "나는 이것을 선호합니다 ..."라는 주관적인 답변을 많이 받게된다는 것입니다. 무의미한 말을하는 대신 개인적인 의견보다는 사실과 참고 문헌으로이 질문에 답하려고 노력할 것입니다.

경험을 통해 일반적으로 사용되지 않는 do-while 대안 (및 goto)을 제외하는 것으로 시작할 수 있습니다. 전문가가 작성한 라이브 프로덕션 코드에서 본 적이 없었습니다.

while(1), while(true)그리고 for(;;)일반적으로 실제 코드에 존재하는 3 개 가지 버전이 있습니다. 물론 완전히 동일하며 동일한 기계 코드를 생성합니다.


for(;;)

  • 이것은 영원한 루프의 원래의 표준적인 예입니다. Kernighan과 Ritchie 의 고대 C 성경 The C Programming Language 에서 다음과 같이 읽을 수 있습니다.

    K & R 2nd ed 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은 모두 생략 할 수 있습니다. 생략 된 expression-2는 0이 아닌 상수로 대체됩니다.

    표준의이 텍스트를 기반으로 대부분의 사람들은 그것이 모호 할뿐만 아니라 미묘하다는 데 동의 할 것이라고 생각합니다. for 루프의 첫 번째와 세 번째 부분이 생략되면 두 번째 부분과 다르게 취급되기 때문입니다.


while(1)

  • 이것은 아마도 for(;;). 그러나 이것은 잘 알려진 규칙이지만 C가 0이 아닌 모든 표현식을 부울 논리 true로 처리한다는 또 다른 모호한 규칙에 의존합니다. 모든 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(;;)인지 알고 있기 때문에 더 읽기 쉽게 만들 필요가 없습니다.


매우 주관적입니다. 나는 이것을 쓴다 :

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

그것의 의도는 매우 명확 하고 읽기도 하기 때문에 당신은 그것을보고 무한 루프가 의도 된 것을 알고 있습니다.

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

동안 while(2), while(3)그리고 while(0.1)동등하게 의미가있다. 그러나 다른 C 프로그래머들과 일치하기 위해 나는를 작성할 것입니다 while(1). 왜냐하면 많은 C 프로그래머들이 이것을 작성하고 표준에서 벗어날 이유를 찾지 못하기 때문입니다.


지루함의 궁극적 인 행동으로 나는 실제로 이러한 루프의 몇 가지 버전을 작성하고 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

그리고 do 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

모두가 좋아하는 것 같습니다 while (true).

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

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

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

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

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

SLaks 에 따르면 그들은 동일하게 컴파일됩니다.

Ben Zotto는 또한 그것이 중요하지 않다고 말합니다 .

빠르지 않습니다. 정말로 관심이 있다면 플랫폼에 대한 어셈블러 출력으로 컴파일하고 확인하십시오. 상관 없습니다. 이것은 중요하지 않습니다. 원하는대로 무한 루프를 작성하십시오.

user1216838에 대한 응답으로 그의 결과를 재현하려는 시도가 있습니다.

내 컴퓨터는 다음과 같습니다.

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

gcc 버전 :

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

그리고 테스트 파일 :

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

명령 :

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

유일한 차이점은 첫 번째 줄인 파일 이름입니다.

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

산출:

        .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

를 사용 -O3하면 출력은 물론 상당히 작지만 여전히 차이는 없습니다.


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

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.

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

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 while were optional, which it isn't, while(); would be strange. while what? By contrast, the answer to the question for what? 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(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 true for while (true).

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

if (condition == true) ...

in favor of:

if (condition) ...

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 predicate to go nicely with "while"; the word "true" isn't a predicate, but the relational operator == is.


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){}.


I use 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.


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.


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.

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


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.

(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.)

모든 합리적인 컴파일러는 무조건적인 점프로 모든 것을 동일한 코드로 컴파일하지만 임베디드 또는 기타 특수 시스템에 대해 불합리한 컴파일러 가 있다고해도 놀라지 않을 것 입니다.


모두 동일한 기능을 수행 할 것이며 선호하는 것을 선택하는 것이 사실입니다. "while (1) 또는 while (true)"를 사용하는 것이 좋습니다.


그들은 동일합니다. 하지만 가장 잘 표현 된 "while (ture)"를 제안합니다.

참조 URL : https://stackoverflow.com/questions/20186809/endless-loop-in-cc

반응형