Program Tip

C 또는 C ++에서 좋은 gotos의 예

programtip 2020. 10. 8. 18:50
반응형

C 또는 C ++에서 좋은 gotos의 예


이 스레드에서 우리 goto는 C 또는 C ++ 의 좋은 사용 예를 살펴 봅니다 . 사람들이 내가 농담이라고 생각했기 때문에 투표 한 답변 에서 영감을 얻었 습니다.

요약 (의도를 더 명확하게하기 위해 라벨이 원본에서 변경됨) :

infinite_loop:

    // code goes here

goto infinite_loop;

대안보다 나은 이유 :

  • 구체적입니다. goto무조건 분기를 일으키는 언어 구조입니다. 대안은 항상 참인 조건이 퇴화 된 조건부 분기를 지원하는 구조를 사용하는 것에 의존합니다.
  • 레이블은 추가 설명없이 의도를 문서화합니다.
  • 독자는 초기의 중간에 코드를 스캔 할 필요가 없습니다 break(여전히 가능하지만 부도덕 한 해커가 시뮬레이션을위한 S를 continue초기에 goto).

규칙 :

  • gotophobes가 이기지 않은 척하십시오. 위의 내용은 기존 관용구에 위배되기 때문에 실제 코드에서 사용할 수 없습니다.
  • 우리 모두가 '유해하다고 생각한 Goto'에 대해 들어 봤고 goto를 사용하여 스파게티 코드를 작성할 수 있다는 것을 알고 있다고 가정합니다.
  • 예에 동의하지 않는 경우 기술적 장점만으로 비판하십시오 ( '사람들이 고토를 좋아하지 않기 때문에'는 기술적 이유가 아닙니다).

우리가 어른처럼 이야기 할 수 있는지 보자.

편집하다

이 질문은 이제 끝난 것 같습니다. 고품질 답변을 생성했습니다. 모든 사람, 특히 내 작은 루프 예제를 진지하게 받아 들인 사람들에게 감사합니다. 대부분의 회의론자들은 블록 범위의 부족을 우려했습니다. @quinmars가 주석에서 지적했듯이 항상 루프 본문을 중괄호로 묶을 수 있습니다. 그 전달에주의 for(;;)하고 while(true)(애 태우게하는 버그를 일으킬 수 있으며이를 생략) 당신이 중 하나를 무료로 중괄호를 제공하지 않습니다. 어쨌든,이 사소한 일에 더 이상 당신의 두뇌 전력의 낭비하지 않습니다 - 나는 무해하고 관용적으로 살 수 for(;;)while(true)(단지뿐만 아니라 내가 내 일을 유지하려는 경우).

다른 응답을 고려할 때 많은 사람들 goto이 항상 다른 방식으로 다시 작성해야하는 것으로 생각합니다. 물론 goto루프, 추가 플래그, 중첩 된 ifs 스택 등을 도입 하여을 피할 수 있지만 goto작업에 가장 적합한 도구 인지 고려하지 않는 이유 는 무엇입니까? 다시 말해, 의도 된 목적을 위해 내장 된 언어 기능을 사용하지 않기 위해 사람들이 얼마나 추악함을 견딜 준비가되어 있습니까? 내 생각은 깃발을 추가하는 것조차 지불하기에는 너무 비싸다는 것입니다. 나는 내 변수가 문제 또는 솔루션 영역에서 사물을 나타내는 것을 좋아합니다. '오로지 피하기 위해 goto'는 그것을 자르지 않습니다.

정리 블록으로 분기하기 위해 C 패턴을 부여한 첫 번째 답변을 수락합니다. IMO, 이것은 goto게시 된 모든 답변 중 가장 강력한 사례를 만듭니다. 분명히 증오자가 그것을 피하기 위해 거쳐야하는 왜곡으로 측정하면 확실합니다.


내가 사람들이 사용하는 것에 대해 들었던 한 가지 트릭이 있습니다. 나는 그것을 야생에서 본 적이 없다. C ++에는 RAII가있어이를보다 관용적으로 수행하기 때문에 C에만 적용됩니다.

void foo()
{
    if (!doA())
        goto exit;
    if (!doB())
        goto cleanupA;
    if (!doC())
        goto cleanupB;

    /* everything has succeeded */
    return;

cleanupB:
    undoB();
cleanupA:
    undoA();
exit:
    return;
}

C에서 GOTO에 대한 고전적인 요구는 다음과 같습니다.

for ...
  for ...
    if(breakout_condition) 
      goto final;

final:

goto없이 중첩 된 루프에서 벗어나는 간단한 방법은 없습니다.


다음은 신호에 의해 중단 될 수있는 Unix 시스템 호출에 대한 어리석지 않은 예제 (Stevens APITUE의)입니다.

restart:
    if (system_call() == -1) {
        if (errno == EINTR) goto restart;

        // handle real errors
    }

대안은 퇴화 루프입니다. 이 버전은 영어 "시스템 호출이 신호에 의해 중단 된 경우 다시 시작하십시오"와 같이 표시됩니다.


Duff의 장치에 goto가 필요하지 않으면 당신도 마찬가지입니다! ;)

void dsend(int count) {
    int n;
    if (!count) return;
    n = (count + 7) / 8;
    switch (count % 8) {
      case 0: do { puts("case 0");
      case 7:      puts("case 7");
      case 6:      puts("case 6");
      case 5:      puts("case 5");
      case 4:      puts("case 4");
      case 3:      puts("case 3");
      case 2:      puts("case 2");
      case 1:      puts("case 1");
                 } while (--n > 0);
    }
}

위키 백과 항목 에서 위의 코드 .


Knuth는 "GOTO 문을 사용한 구조화 된 프로그래밍"이라는 논문을 작성했습니다. 예를 들어 여기 에서 얻을 수 있습니다 . 거기에서 많은 예를 찾을 수 있습니다.


매우 흔한.

do_stuff(thingy) {
    lock(thingy);

    foo;
    if (foo failed) {
        status = -EFOO;
        goto OUT;
    }

    bar;
    if (bar failed) {
        status = -EBAR;
        goto OUT;
    }

    do_stuff_to(thingy);

OUT:
    unlock(thingy);
    return status;
}

내가 사용하는 유일한 경우 goto는 앞으로 점프하는 것입니다. 일반적으로 블록에서 벗어나 블록으로 들어 가지 않습니다. 이렇게하면 do{}while(0)읽기 가능하고 구조화 된 코드를 유지하면서 중첩을 증가시키는 기타 구문의 남용을 방지 할 수 있습니다.


나는 일반적으로 gotos에 대해 아무것도 가지고 있지 않지만 언급 한 것처럼 루프에 사용하지 않는 몇 가지 이유를 생각할 수 있습니다.

  • 범위를 제한하지 않으므로 내부에서 사용하는 임시 변수는 나중에까지 해제되지 않습니다.
  • 범위를 제한하지 않으므로 버그가 발생할 수 있습니다.
  • 범위를 제한하지 않으므로 나중에 동일한 범위의 코드에서 동일한 변수 이름을 다시 사용할 수 없습니다.
  • 범위를 제한하지 않으므로 변수 선언을 건너 뛸 수 있습니다.
  • 사람들은 그것에 익숙하지 않으며 코드를 읽기 어렵게 만듭니다.
  • 이 유형의 중첩 루프는 스파게티 코드로 이어질 수 있으며 노멀 루프는 스파게티 코드로 이어지지 않습니다.

goto를 사용하기에 좋은 곳 중 하나는 여러 지점에서 중단 할 수있는 절차에 있으며, 각 지점에는 다양한 수준의 정리가 필요합니다. Gotophobes는 항상 구조화 된 코드와 일련의 테스트로 gotos를 대체 할 수 있지만 과도한 들여 쓰기를 제거하기 때문에 이것이 더 간단하다고 생각합니다.

if (! openDataFile ())
  goto quit;

if (! getDataFromFile ())
  closeFileAndQuit로 이동합니다.

if (! allocateSomeResources)
  freeResourcesAndQuit로 이동하십시오.

// 여기서 더 많은 작업을 수행합니다 ....

freeResourcesAndQuit :
   // 무료 리소스
closeFileAndQuit :
   // 파일 닫기
떠나다:
   // 종료!

@ fizzer.myopenid.com : 게시 된 코드 스 니펫은 다음과 같습니다.

    while (system_call() == -1)
    {
        if (errno != EINTR)
        {
            // handle real errors

            break;
        }
    }

나는 확실히이 형태를 선호한다.


시간이 지남에 따라이 패턴을 싫어하게되었지만 COM 프로그래밍에 뿌리를두고 있습니다.

#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
  HRESULT hr = S_OK;
  IfFailGo( pFoo->PerformAction() );
  IfFailGo( pFoo->SomeOtherAction() );
Error:
  return hr;
}

다음은 좋은 goto의 예입니다.

// No Code

goto가 올바르게 사용되는 것을 보았지만 상황은 일반적으로 추악합니다. goto자체 의 사용 이 원본보다 훨씬 더 나쁠 때만 그렇습니다. @Johnathon Holland는 당신이 버전이 덜 명확하다는 것입니다. 사람들은 지역 변수를 두려워하는 것 같습니다.

void foo()
{
    bool doAsuccess = doA();
    bool doBsuccess = doAsuccess && doB();
    bool doCsuccess = doBsuccess && doC();

    if (!doCsuccess)
    {
        if (doBsuccess)
            undoB();
        if (doAsuccess)
            undoA();
    }
}

그리고 저는 이와 같은 루프를 선호하지만 어떤 사람들은 while(true).

for (;;)
{
    //code goes here
}

My gripe about this is that you lose block scoping; any local variables declared between the gotos remains in force if the loop is ever broken out of. (Maybe you're assuming the loop runs forever; I don't think that's what the original question writer was asking, though.)

The problem of scoping is more of an issue with C++, as some objects may be depending on their dtor being called at appropriate times.

For me, the best reason to use goto is during a multi-step initialization process where the it's vital that all inits are backed out of if one fails, a la:

if(!foo_init())
  goto bye;

if(!bar_init())
  goto foo_bye;

if(!xyzzy_init())
  goto bar_bye;

return TRUE;

bar_bye:
 bar_terminate();

foo_bye:
  foo_terminate();

bye:
  return FALSE;

I don't use goto's myself, however I did work with a person once that would use them in specific cases. If I remember correctly, his rationale was around performance issues - he also had specific rules for how. Always in the same function, and the label was always BELOW the goto statement.


#include <stdio.h>
#include <string.h>

int main()
{
    char name[64];
    char url[80]; /*The final url name with http://www..com*/
    char *pName;
    int x;

    pName = name;

    INPUT:
    printf("\nWrite the name of a web page (Without www, http, .com) ");
    gets(name);

    for(x=0;x<=(strlen(name));x++)
        if(*(pName+0) == '\0' || *(pName+x) == ' ')
        {
            printf("Name blank or with spaces!");
            getch();
            system("cls");
            goto INPUT;
        }

    strcpy(url,"http://www.");
    strcat(url,name);
    strcat(url,".com");

    printf("%s",url);
    return(0);
}

@Greg:

Why not do your example like this:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}

참고URL : https://stackoverflow.com/questions/245742/examples-of-good-gotos-in-c-or-c

반응형