문제

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

요약 (레이블이 원본에서 의도를 더 명확하게 변경했습니다) :

infinite_loop:

    // code goes here

goto infinite_loop;

대안보다 더 나은 이유 :

  • 구체적입니다. goto 무조건 분기를 일으키는 언어 구성입니다. 대안은 조건부 분기를지지하는 구조를 사용하고 항상 종료 조건을 퇴화시키는 데 달려 있습니다.
  • 이 레이블은 추가 의견없이 의도를 문서화합니다.
  • 독자는 초기에 중재 코드를 스캔 할 필요가 없습니다. breaks (원하지 않는 해커가 시뮬레이션 할 수는 있지만 여전히 가능합니다.continue 일찍 goto).

규칙 :

  • gotophobes가 이기지 못한 척하십시오. 위의 내용은 확립 된 관용구에 반대하기 때문에 실제 코드에서 사용할 수 없다는 것을 이해합니다.
  • 우리 모두가 '유해한 고려'에 대해 들었고 Goto가 스파게티 코드를 작성하는 데 사용될 수 있다는 것을 알고 있다고 가정합니다.
  • 당신이 모범에 동의하지 않는다면, 기술적 인 장점에 대해서만 비판하십시오 ( '사람들이 goto를 좋아하지 않기 때문에'는 기술적 인 이유가 아닙니다).

우리가 어른처럼 이것에 대해 이야기 할 수 있는지 봅시다.

편집하다

이 질문은 지금 완성 된 것 같습니다. 고품질의 답변을 생성했습니다. 모든 사람, 특히 작은 루프 예제를 진지하게 받아 들인 분들께 감사드립니다. 대부분의 회의론자들은 블록 범위가 부족한 것에 대해 우려했다. @Quinmars가 의견에서 지적했듯이, 항상 루프 본체 주위에 버팀대를 넣을 수 있습니다. 나는 그것을 전달할 때 주목한다 for(;;) 그리고 while(true) 중괄호를 무료로주지 마십시오 (그리고 생략하면 vexing 버그가 발생할 수 있습니다). 어쨌든, 나는이 사소한 것에 더 이상 당신의 두뇌 힘을 낭비하지 않을 것입니다 - 나는 무해하고 관용적으로 살 수 있습니다. 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())
  goto closeFileAndQuit;

if (!allocateSomeResources)
  goto freeResourcesAndQuit;

// Do more work here....

freeResourcesAndQuit:
   // free resources
closeFileAndQuit:
   // close file
quit:
   // quit!

@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 poblem은 당신이 버전이 덜 명확하다는 것입니다. 사람들은 지역 변수를 두려워하는 것 같습니다.

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
}

이것에 대한 나의 그립은 당신이 블록 범위를 잃는다는 것입니다. 루프가 고장 나면 GOTOS간에 선언 된 모든 로컬 변수는 여전히 유효합니다. (아마도 당신은 루프가 영원히 실행된다고 가정 할 것입니다. 그래도 원래 질문 작가가 묻는 것이 아니라고 생각합니다.)

스코핑의 문제는 C ++의 문제가 더 중요합니다. 일부 객체는 적절한 시간에 DTOR가 호출되는 것에 의존 할 수 있기 때문입니다.

저에게있어 Goto를 사용해야하는 가장 좋은 이유는 다단계 초기화 프로세스 중에 모든 인트가 실패하면 백업하는 것이 중요합니다.

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;

나는 Goto의 나 자신을 사용하지 않지만 특정 경우에 사용하는 사람과 함께 일했습니다. 내가 정확하게 기억한다면, 그의 근거는 성과 문제에 관한 것이었다. 그는 또한 특정 규칙을 가졌다. 어떻게. 항상 같은 함수에 있으며 레이블은 항상 GOTO 문보다 낮았습니다.

#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 :

왜 다음과 같이 예를 들이지 않습니까?

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top