문제

다들 다익스트라(Dijkstra)를 알고 계시죠? 편집자에게 보내는 편지:유해한 것으로 간주되는 진술로 이동 (또한 여기 .html 성적표 및 여기 .pdf) 그리고 그 이후로 가능할 때마다 goto 문을 피하려는 엄청난 노력이 있었습니다.goto를 사용하여 유지 관리가 불가능하고 확장되는 코드를 생성하는 것이 가능하지만 그럼에도 불구하고 여전히 남아 있습니다. 현대 프로그래밍 언어.심지어 고급 계속 Scheme의 제어 구조는 정교한 goto로 설명할 수 있습니다.

어떤 상황에서 goto를 사용해야 합니까?언제 피하는 것이 가장 좋나요?

후속 질문으로 :C는 현재 스택 프레임뿐만 아니라 호출 프레임 내에서도 이동할 수 있는 기능을 제공하는 setjmp 및 longjmp 함수 쌍을 제공합니다.이것들은 goto만큼 위험한 것으로 간주되어야 합니까?더 위험한?


Dijkstra 자신은 그 타이틀에 대해 후회했지만 책임은 없습니다.끝에 EWD1308 (또한 여기 .pdf) 그는 다음과 같이 썼습니다.

마지막으로 기록을 위한 짧은 이야기입니다.1968 년 ACM의 커뮤니케이션은 제목에 따라 광산의 텍스트를 출판했습니다. "GoTo 성명서는 유해하다고 생각했습니다"나중에 몇 년 동안 유감스럽게도 유감스럽게도, 종종 그 제목보다 더 이상 그것을 보지 못한 저자들에 의해 종종 템플릿이되어 나의 명성의 초석이되었습니다.우리는 "dijkstra가 유해하다"이라는 제목을 포함하여 거의 모든 X에 대해 "x 고려"라는 제목에 따라 모든 종류의 기사를 볼 것입니다.그러나 무슨 일이 일어 났습니까?나는 제목에 따라 논문을 제출했다.GOTO 진술에 대한 사건", 출판물 속도를 높이기 위해 편집자는"편집자에게 보낸 편지 "로 바뀌었고, 그 과정에서 그는 자신의 발명의 새로운 제목을 주었다!편집자는 Niklaus Wirth였습니다.

Dijkstra의 논문에 필적할 만한 이 주제에 관해 잘 생각된 고전 논문은 다음과 같습니다. go to 문을 사용한 구조적 프로그래밍, 도널드 E.크누스.두 가지를 모두 읽는 것은 주제에 대한 맥락과 비독단적인 이해를 다시 확립하는 데 도움이 됩니다.이 논문에서는 이 사건에 대한 Dijkstra의 의견이 보고되었으며 더욱 강력해졌습니다.

도널드 E.크누스: 나는 그러한 견해를 제시함으로써 실제로 다음을 썼기 때문에 Dijkstra의 아이디어에 크게 동의하지 않는다고 생각합니다."제발 [진술에 대한 Go To 성명]에 대해 내가 끔찍한 독단적이라고 믿는 함정에 빠지지 마십시오. 나는 다른 사람들이 간단한 형태의 코딩 징계로 단일 트릭으로 해결 될 수있는 것처럼 다른 사람들이 종교를 만들고 있다는 불편한 느낌을 가지고 있습니다!"

도움이 되었습니까?

해결책

다음 진술은 일반화입니다.예외를 주장하는 것은 언제나 가능하지만 일반적으로 (내 경험과 겸손한 의견으로는) 위험을 감수할 가치가 없습니다.

  1. 메모리 주소(GOTO 또는 원시 포인터)를 제한 없이 사용하면 쉽게 피할 수 있는 실수를 저지를 수 있는 기회가 너무 많습니다.
  2. 코드의 특정 "위치"에 도달하는 방법이 많을수록 해당 지점의 시스템 상태에 대한 확신이 낮아질 수 있습니다.(아래를 참조하세요.)
  3. 구조적 프로그래밍 IMHO는 "GOTO 방지"에 관한 것이 아니라 코드 구조를 데이터 구조와 일치시키는 것에 관한 것입니다.예를 들어, 반복되는 데이터 구조(예:배열, 순차파일 등)은 자연스럽게 반복되는 코드 단위로 처리됩니다.내장된 구조(예:while, for, Until, for-each 등)을 사용하면 프로그래머는 동일한 진부한 코드 패턴을 반복하는 지루함을 피할 수 있습니다.
  4. GOTO가 낮은 수준의 구현 세부 사항이라 할지라도(항상 그런 것은 아닙니다!) 프로그래머가 생각해야 하는 수준보다 낮습니다.개인 수표장을 원시 바이너리로 균형을 맞추는 프로그래머는 몇 명입니까?단순히 데이터베이스 엔진에 키를 제공하는 대신 디스크의 어느 섹터에 특정 레코드가 포함되어 있는지 걱정하는 프로그래머가 몇 명이나 됩니까(그리고 실제로 모든 프로그램을 물리적 디스크 섹터 측면에서 작성했다면 문제가 발생할 수 있는 경우는 얼마나 됩니까?)

위의 각주:

포인트 2와 관련하여 다음 코드를 고려하십시오.

a = b + 1
/* do something with a */

코드의 "무언가 수행" 지점에서 우리는 다음과 같이 매우 확신을 가지고 말할 수 있습니다. a 보다 크다 b.(예, 트랩되지 않은 정수 오버플로 가능성을 무시하고 있습니다.간단한 예를 들어 설명하지 않겠습니다.)

반면에 코드가 다음과 같이 읽혀졌다면:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

라벨 10에 도달하는 방법이 다양하다는 것은 우리가 둘 사이의 관계에 대해 확신을 가지기 위해 훨씬 더 열심히 노력해야 함을 의미합니다. a 그리고 b 그 시점에.(사실 일반적인 경우에는 결정이 불가능합니다!)

4번 항목과 관련하여 코드에서 "어딘가로 이동"이라는 전체 개념은 단지 비유일 뿐입니다.전자와 광자(폐열용)를 제외하고 CPU 내부에는 실제로 아무 것도 "가는" 일이 없습니다.때때로 우리는 더 유용한 또 다른 비유를 포기합니다.나는 (몇십 년 전에!) 언어를 접한 것을 기억합니다.

if (some condition) {
  action-1
} else {
  action-2
}

action-1과 action-2를 라인 외부 루틴으로 컴파일한 다음 조건의 부울 값을 사용하여 둘 중 하나를 호출하는 단일 2인자 VM opcode를 사용하여 가상 머신에서 구현되었습니다.개념은 "여기로 가거나 저기로 가다"가 아니라 단순히 "지금 무엇을 호출할지 선택"이었습니다.다시 말하지만, 단지 은유의 변화일 뿐입니다.

다른 팁

XKCD's GOTO Comic

내 동료는 GOTO를 사용하는 유일한 이유는 그것이 유일한 탈출구라고 너무 멀리 프로그램해 놓은 경우라고 말했습니다.즉, 미리 적절한 디자인을 하면 나중에 GOTO를 사용할 필요가 없습니다.

나는이 만화가 아름답게 "프로그램의 흐름을 재구성하거나 대신 하나의 작은 'goto'를 사용할 수 있다고 생각했다"고 생각했다. GOTO는 디자인이 약할 때 약한 방법입니다. 벨로시랩터는 약한자를 잡아먹는다.

때로는 단일 함수 내에서 예외 처리 대신 GOTO를 사용하는 것이 유효할 수 있습니다.

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

COM 코드는 상당히 자주 이 패턴에 속하는 것 같습니다.

goto를 한 번만 사용한 기억이 납니다.일련의 5개의 중첩된 카운트 루프가 있었고 특정 조건에 따라 초기에 내부에서 전체 구조를 깨뜨릴 수 있어야 했습니다.

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

부울 중단 변수를 쉽게 선언하고 이를 각 루프에 대한 조건의 일부로 사용할 수도 있었지만 이 경우 GOTO가 실용적이고 읽기 쉽다고 결정했습니다.

어떤 벨로시랩터도 나를 공격하지 않았습니다.

우리는 이미 이것을 가지고 있었다 논의 그리고 난 옆에 있어 내 입장.

더욱이, 나는 더 높은 수준의 언어 구조를 "goto 변장하고 있어” 왜냐하면 그들은 분명히 요점을 이해하지 못하기 때문입니다. 조금도.예를 들어:

Scheme의 고급 연속 제어 구조도 정교한 goto로 설명할 수 있습니다.

그것은 완전히 말도 안되는 소리입니다. 모든 제어 구조는 다음과 같은 측면에서 구현될 수 있습니다. goto 그러나 이 관찰은 아주 사소하고 쓸모가 없습니다. goto 긍정적인 효과 때문에 해로운 것으로 간주되지 않지만 부정적인 결과 때문에 이러한 현상은 구조화된 프로그래밍에 의해 제거되었습니다.

마찬가지로, "GOTO는 도구이며 모든 도구와 마찬가지로 사용되거나 남용될 수 있습니다"라고 말하는 것은 완전히 옳지 않습니다.현대 건설 노동자는 암석을 사용하여“도구”라고 주장하지 않을 것입니다. 바위는 망치로 대체되었습니다. goto 제어 구조로 대체되었습니다.건설 노동자가 망치 없이 야생에 발이 묶였다면 당연히 돌을 대신 사용할 것입니다.프로그래머가 X 기능이 없는 열등한 프로그래밍 언어를 사용해야 한다면, 물론 그녀는 다음을 사용해야 할 수도 있습니다. goto 대신에.그러나 그녀가 적절한 언어 기능 대신 다른 곳에서 그것을 사용한다면 그녀는 분명히 언어를 제대로 이해하지 못하고 잘못 사용하고 있는 것입니다.정말 그렇게 간단합니다.

Goto는 단지 프로그램을 위해 프로그램에 포함시킬 항목 목록에서 극히 적습니다.그렇다고 받아 들일 수 없다는 의미는 아닙니다.

Goto는 상태 머신에 적합할 수 있습니다.루프의 스위치 문은 다음과 같습니다(일반적으로 중요한 순서대로).(a) 실제로 제어 흐름을 나타내지 않습니다. (b) 추악하며, (c) 언어 및 컴파일러에 따라 잠재적으로 비효율적입니다.따라서 상태 당 하나의 기능을 작성하고 "Next_state return"과 같은 일을합니다. Goto처럼 보입니다.

물론, 상태 머신을 이해하기 쉬운 방식으로 코딩하는 것은 어렵습니다.그러나 그 어려움은 goto를 사용하는 것과 관련이 없으며 대체 제어 구조를 사용해도 그 어려움을 줄일 수 없습니다.귀하의 언어에 '상태 기계' 구조가 없다면 말이죠.내 것은 그렇지 않습니다.

더 구체적인 제어 흐름(루프, 조건부 등)보다는 제한된 허용 가능한 전환 세트(gotos)로 연결된 일련의 노드(상태)를 통한 경로 측면에서 알고리즘이 실제로 가장 이해하기 쉬운 드문 경우입니다. ), 코드에 명시적으로 명시되어야 합니다.그리고 예쁜 도표를 그려야 해요.

setjmp/longjmp는 예외 또는 예외와 유사한 동작을 구현하는 데 유용할 수 있습니다.보편적으로 칭찬되지는 않지만 예외는 일반적으로 "유효한" 제어 구조로 간주됩니다.

setjmp/longjmp는 올바르게 사용하기가 더 어렵다는 점에서 goto보다 '더 위험'합니다.

나쁜 코드를 작성하는 것이 가장 어렵지 않은 언어는 없었으며 결코 없었습니다.-- 도널드 커누스.

C에서 goto를 사용한다고 해서 C에서 좋은 코드를 작성하는 것이 더 쉬워지는 것은 아닙니다.사실, C가 다음과 같은 점을 놓치는 것이 낫습니다. 추정된 영광스러운 어셈블러 언어로 작동할 수 있습니다.

다음은 "유해한 것으로 간주되는 포인터", "유해한 것으로 간주되는 오리 타이핑"입니다.그렇다면 안전하지 않은 프로그래밍 구조를 빼앗으러 올 때 누가 당신을 방어할 것입니까?뭐라고?

~ 안에 리눅스:커널 코드에서 goto 사용 Kernel Trap에는 Linux 코드에서 GOTO를 사용하는 것에 대해 Linus Torvalds 및 "새로운 사람"과의 토론이 있습니다.거기에는 아주 좋은 점이 몇 가지 있는데, 리누스는 평소의 오만함을 차려입고 있습니다 :)

일부 구절:

리누스:"아니요, 당신은 Niklaus Wirth가 실제로 자신이 말하는 것을 알고 있다고 생각한 CS 사람들에 의해 세뇌되었습니다.그는 그렇지 않았습니다.그는 냉담한 단서가 없습니다. "

-

리누스:"나는 Goto 's가 괜찮다고 생각하며, 종종 많은 양의 들여 쓰기보다 더 읽기 쉬운다."

-

리누스:"물론 라벨이 설명 할 수없는 파스칼과 같은 어리석은 언어에서는 Goto 's가 나빠질 수 있습니다."

C에서는, goto 잠재적인 버그를 현지화하는 경향이 있는 현재 기능의 범위 내에서만 작동합니다. setjmp 그리고 longjmp 훨씬 더 위험하고 비지역적이고 복잡하며 구현에 따라 다릅니다.그러나 실제로는 너무 모호하고 흔하지 않아 많은 문제를 일으킬 수 없습니다.

나는 위험하다고 믿는다. goto C에서는 크게 과장되었습니다.원본은 기억해두세요 goto 논쟁은 초보자가 다음과 같이 스파게티 코드를 작성했던 구식 BASIC과 같은 언어 시절에 발생했습니다.

3420 IF A > 2 THEN GOTO 1430

여기서 Linus는 다음의 적절한 사용법을 설명합니다. goto: http://www.kernel.org/doc/Documentation/CodingStyle (7장).

오늘은 큰 사건을 보기가 어렵습니다. GOTO "구조적 프로그래밍" 사람들이 대부분 논쟁에서 이겼고 오늘날의 언어에는 피할 수 있는 충분한 제어 흐름 구조가 있기 때문입니다. GOTO.

수를 세어보세요 goto현대 C 프로그램에 있습니다.이제 개수를 추가하세요. break, continue, 그리고 return 진술.또한 사용 횟수를 추가하십시오. if, else, while, switch 또는 case.그 정도쯤이야 GOTODijkstra가 편지를 썼던 1968년에 FORTRAN이나 BASIC으로 글을 쓰고 있었다면 프로그램이 그랬을 것입니다.

당시 프로그래밍 언어에는 제어 흐름이 부족했습니다.예를 들어 원래 Dartmouth BASIC에서는 다음과 같습니다.

  • IF 진술에는 없었다 ELSE.원하는 경우 다음과 같이 작성해야 합니다.

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • 당신의 IF 진술에는 필요하지 않았습니다. ELSE, 여전히 한 줄로 제한되어 있었는데, 일반적으로 다음과 같은 내용으로 구성되었습니다. GOTO.

  • 없었다 DO...LOOP 성명.비-FOR 루프를 종료하려면 명시적으로 루프를 종료해야 했습니다. GOTO 또는 IF...GOTO 처음으로 돌아갑니다.

  • 없었다 SELECT CASE.당신은 사용해야했다 ON...GOTO.

그래서 당신은 결국 많은 ~의 GOTO귀하의 프로그램에 있습니다.그리고 당신은 GOTO단일 서브루틴 내에 있어야 합니다(왜냐하면 GOSUB...RETURN 서브루틴 개념이 너무 약해서) GOTO갈 수도 있어 어딘가에.분명히 이로 인해 제어 흐름을 따르기가 어려워졌습니다.

이곳은 안티가 있는 곳이다.GOTO 움직임이 나왔습니다.

Go To는 특정 경우에 "실제" 예외 처리를 위한 일종의 대체 기능을 제공할 수 있습니다.고려하다:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

분명히 이 코드는 공간을 덜 차지하도록 단순화되었으므로 세부 사항에 너무 집착하지 마십시오.하지만 내가 너무 많이 본 대안을 생각해 보세요. 생산 goto 사용을 피하기 위해 터무니없는 길이의 코더가 코드를 작성했습니다.

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

이제 기능적으로 이 코드는 완전히 동일한 작업을 수행합니다.실제로 컴파일러에서 생성된 코드는 거의 동일합니다.그러나 달래려는 프로그래머의 열정에 노고토 (학문적 질책의 무서운 신), 이 프로그래머는 기본 관용어를 완전히 깨뜨렸습니다. while 루프는 코드의 가독성을 실수로 표현하고 수행했습니다. 이것은 더 좋지 않습니다.

따라서 이야기의 교훈은 goto를 사용하지 않기 위해 정말 어리석은 짓을 하고 있다면 사용하지 말라는 것입니다.

도널드 E.Knuth는 1992년 CSLI의 "Literate 프로그래밍"이라는 책에서 이 질문에 답했습니다.페이지에.17 에세이가 있어요 "goto 문을 사용한 구조적 프로그래밍"(PDF).아마 다른 책에도 이 글이 실렸을 것 같아요.

이 기사에서는 Dijkstra의 제안을 설명하고 이것이 유효한 상황을 설명합니다.그러나 그는 또한 구조화된 루프만으로는 쉽게 재현할 수 없는 여러 가지 반대 사례(문제 및 알고리즘)를 제공합니다.

이 기사에는 문제에 대한 완전한 설명, 기록, 예시 및 반박 사례가 포함되어 있습니다.

Jay Ballou가 답변을 추가하는 것에 매료되어 £0.02를 추가하겠습니다.Bruno Ranschaert가 아직 그렇게 하지 않았다면 Knuth의 "GOTO 문을 사용한 구조적 프로그래밍" 기사를 언급했을 것입니다.

제가 본 적이 없는 것 중 하나는 비록 일반적이지는 않지만 Fortran 교과서에서 가르치는 일종의 코드입니다.DO 루프의 확장된 범위 및 개방형 코드 서브루틴과 같은 것입니다(기억하십시오. 이는 Fortran 77 또는 90이 아니라 Fortran II, Fortran IV 또는 Fortran 66입니다).최소한 구문 세부사항이 부정확할 가능성은 있지만 개념은 충분히 정확해야 합니다.각 경우의 스니펫은 단일 함수 내에 있습니다.

우수하지만 날짜가 지난(그리고 절판된) 책 '프로그래밍 스타일의 요소, 2판Kernighan & Plauger의 '에는 해당 시대(70년대 후반)의 프로그래밍 교과서에서 GOTO를 남용한 실제 사례가 포함되어 있습니다.그러나 아래 자료는 그 책에서 나온 것이 아닙니다.

DO 루프의 확장된 범위

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

그런 말도 안되는 이유 중 하나는 좋은 구식 펀치 카드였습니다.레이블(표준 스타일이었기 때문에 순서가 잘 맞지 않음!)이 열 1에 있고(실제로는 열 1-5에 있어야 함) 코드가 열 7-72에 있음(열 6은 계속됨)을 알 수 있습니다. 마커 열).73-80열에는 일련 번호가 부여되며, 펀치 카드 데크를 일련 번호 순서로 정렬하는 기계가 있었습니다.시퀀스된 카드에 프로그램이 있고 루프 중간에 몇 개의 카드(라인)를 추가해야 하는 경우 추가 라인 이후의 모든 항목을 다시 펀치해야 합니다.그러나 하나의 카드를 GOTO 항목으로 교체하면 모든 카드의 순서를 다시 지정하는 것을 피할 수 있습니다. 루틴이 끝날 때 새 일련 번호로 새 카드를 집어넣으면 됩니다.이를 '친환경 컴퓨팅'에 대한 최초의 시도, 즉 펀치 카드 절약(또는 더 구체적으로 재입력 노동력 절약 및 그에 따른 재입력 ​​오류 절약)이라고 생각하십시오.

아, 제가 소리 지르지 않고 바람을 피우고 있다는 점을 아실 수도 있습니다. Fortran IV는 일반적으로 모두 대문자로 작성되었습니다.

오픈 코딩된 서브루틴

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

레이블 76과 54 사이의 GOTO는 계산된 goto 버전입니다.변수 i의 값이 1이면 목록의 첫 번째 레이블로 이동합니다(123).값이 2이면 두 번째로 이동하는 식입니다.76에서 계산된 goto까지의 조각은 개방형 코드 서브루틴입니다.이는 서브루틴처럼 실행되지만 함수 본문에 작성된 코드 조각이었습니다.(Fortran에는 한 줄에 딱 맞는 내장 함수인 명령문 함수도 있었습니다.)

계산된 goto보다 더 나쁜 구조가 있었습니다. 변수에 레이블을 할당한 다음 할당된 goto를 사용할 수 있었습니다.인터넷 검색 할당된 이동 Fortran 95에서 삭제되었다고 하더군요.Dijkstra의 "GOTO는 유해한 것으로 간주됨" 편지나 기사를 통해 공개적으로 시작되었다고 말할 수 있는 구조화된 프로그래밍 혁명에 대해 생각해 보세요.

Fortran에서 수행된 일들에 대한 지식이 없으면(그리고 대부분이 당연히 중도에서 제외된 다른 언어에서) Dijkstra가 다루고 있는 문제의 범위를 이해하기가 어렵습니다.도대체 나는 그 편지가 출판된 지 10년이 지나서야 프로그래밍을 시작했습니다(그러나 한동안 Fortran IV에서 프로그래밍할 수 있는 불행한 일도 있었습니다).

Goto는 도움이 된다고 생각했습니다.

나는 1975년에 프로그래밍을 시작했습니다.1970년대 프로그래머들에게 "goto는 해롭다고 간주된다"는 말은 현대 제어 구조를 갖춘 새로운 프로그래밍 언어가 시도해 볼 가치가 있다는 것을 의미했습니다.우리는 새로운 언어를 시도해 보았습니다.우리는 빨리 개종했습니다.우리는 결코 돌아 가지 않았습니다.

우리는 다시는 가지 않았지만, 당신이 더 어리다면 애초에 그곳에 가본 적이 없을 것입니다.

이제 고대 프로그래밍 언어에 대한 배경 지식은 프로그래머의 나이를 나타내는 지표가 아닌 이상 그다지 유용하지 않을 수 있습니다.그럼에도 불구하고, 젊은 프로그래머들은 이러한 배경 지식이 부족하기 때문에 "해로운 것으로 간주되는 Goto"라는 슬로건이 전달하는 메시지를 더 이상 이해하지 못합니다. 소개 당시 의도한 청중에게.

이해하지 못하는 슬로건은 그다지 밝지 않습니다.아마도 그러한 슬로건은 잊어버리는 것이 가장 좋을 것입니다.그런 슬로건은 도움이 되지 않습니다.

그러나 "고토는 해롭다고 생각한다"라는 특별한 슬로건은 그 자체로 언데드 생명을 띠고 있습니다.

학대받지 않을 수 있나요?답변:물론이죠, 하지만 그래서 어쩌죠?실질적으로 모든 프로그래밍 요소 ~할 수 있다 학대를 당하다.겸손한 bool 예를 들어, 우리 중 일부가 믿고 싶어하는 것보다 더 자주 학대를 당합니다.

대조적으로, 나는 1990년 이후 goto 남용에 대한 실제 사례를 단 한 번도 만난 적이 없습니다.

goto의 가장 큰 문제는 아마도 기술적 문제가 아니라 사회적 문제일 것입니다.잘 모르는 프로그래머는 때때로 goto를 사용하지 않는 것이 자신을 똑똑하게 들리게 한다고 느끼는 것 같습니다.때때로 그러한 프로그래머를 만족시켜야 할 수도 있습니다.인생도 마찬가지다.

오늘날 goto의 가장 나쁜 점은 충분히 사용되지 않는다는 것입니다.

그런 것은 없습니다 유해한 것으로 간주되는 GOTO.

GOTO는 도구이며, 모든 도구와 마찬가지로 사용할 수 있고 학대.

그러나 프로그래밍 세계에는 다음과 같은 경향이 있는 도구가 많이 있습니다. 학대 존재 그 이상 사용된, GOTO도 그 중 하나입니다.그만큼 와 함께 델파이의 진술은 또 다른 것입니다.

개인적으로 둘 다 사용하지 않습니다 일반적인 코드에서, 하지만 나는 둘 다 이상하게 사용했습니다 이동 그리고 와 함께 이는 보증되었으며 대체 솔루션에는 더 많은 코드가 포함되었을 것입니다.

가장 좋은 해결책은 컴파일러가 키워드가 다음과 같다고 경고하는 것입니다. 더럽혀진, 경고를 제거하려면 명령문 주위에 몇 가지 pragma 지시문을 채워야 합니다.

마치 아이들에게 이렇게 말하는 것과 같습니다. 가위로 뛰지 마세요.가위는 나쁘지 않지만 일부 사용법은 건강을 유지하는 최선의 방법이 아닐 수도 있습니다.

내가 리눅스 커널에서 몇 가지 일을 하기 시작한 이후로, gotos는 예전만큼 나를 괴롭히지 않았습니다.처음에 나는 그들(커널 담당자)이 내 코드에 gotos를 추가하는 것을 보고 다소 놀랐습니다.그 이후로 저는 일부 제한된 상황에서 gotos를 사용하는 데 익숙해졌고 이제 가끔 직접 사용하게 될 것입니다.일반적으로 함수의 여러 위치에서 동일한 정리 및 구제 조치를 복제하는 대신 일종의 정리 및 구제 조치를 수행하기 위해 함수의 끝으로 점프하는 goto입니다.그리고 일반적으로 다른 기능으로 넘겨줄 만큼 큰 것이 아닙니다.로컬로 (k)malloc된 변수를 해제하는 것이 일반적인 경우입니다.

나는 setjmp/longjmp를 한 번만 사용하는 코드를 작성했습니다.그것은 MIDI 드럼 시퀀서 프로그램에 있었습니다.재생은 모든 사용자 상호 작용과 별도의 프로세스에서 발생했으며 재생 프로세스는 재생에 필요한 제한된 정보를 얻기 위해 UI 프로세스와 공유 메모리를 사용했습니다.사용자가 재생을 중지하고 싶을 때 재생 프로세스는 사용자가 중지하기를 원할 때 실행 중이던 위치를 복잡하게 풀기보다는 그냥 longjmp "처음으로 돌아가기"를 수행하여 다시 시작했습니다.훌륭하게 작동하고 간단했으며, 그 경우에는 이와 관련된 문제나 버그가 전혀 없었습니다.

setjmp/longjmp에는 그 자리가 있습니다. 하지만 그 곳은 여러분이 방문할 가능성이 없지만 아주 오랫동안 한 번은 방문하게 될 곳입니다.

편집하다:방금 코드를 살펴봤습니다.실제로 내가 사용한 것은 longjmp가 아닌 siglongjmp()였습니다(큰 문제는 아니지만 siglongjmp가 존재한다는 사실조차 잊어버렸습니다.)

당신이 스스로 생각할 수 있는 한 결코 그렇지 않았습니다.

C로 VM을 작성하는 경우 다음과 같이 (gcc의) 계산된 gotos를 사용하는 것으로 나타났습니다.

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

루프 내부의 기존 스위치보다 훨씬 빠르게 작동합니다.

왜냐하면 goto 혼란스러운 메타프로그래밍에 사용될 수 있음

Goto 둘 다 높은 레벨 그리고 낮은 수준의 컨트롤 표현이 부족하고 결과적으로 대부분의 문제에 적합한 적절한 디자인 패턴이 없습니다.

그것은 낮은 수준의 goto는 다음과 같은 더 높은 것을 구현하는 기본 작업이라는 의미에서 while 또는 foreach 또는 뭔가.

그것은 높은 레벨 특정 방식으로 사용될 때 구조화된 루프를 제외하고 중단 없이 명확한 순서로 실행되는 코드를 취하여 이를 충분한 논리 조각으로 변경한다는 의미에서 gotos, 동적으로 재조립되는 로직의 가방입니다.

그래서, 산문적인 그리고 사악한 옆으로 goto.

그만큼 단조로운 측면 위쪽을 가리키는 goto는 완벽하게 합리적인 루프를 구현할 수 있고 아래쪽을 가리키는 goto는 완벽하게 합리적인 루프를 구현할 수 있다는 것입니다. break 또는 return.물론, 실제 while, break, 또는 return 불쌍한 인간은 효과를 시뮬레이션할 필요가 없기 때문에 훨씬 더 읽기 쉬울 것입니다. goto 큰 그림을 얻으려면.따라서 일반적으로 나쁜 생각입니다.

그만큼 사악한 편 while, break 또는 return에 goto를 사용하지 않고 호출되는 용도로 사용하는 루틴이 포함됩니다. 스파게티 논리.이 경우 goto를 좋아하는 개발자는 goto의 미로에서 코드 조각을 구성하고 있으며 이를 이해하는 유일한 방법은 전체적으로 정신적으로 시뮬레이션하는 것입니다. goto가 많을 때 매우 피곤한 작업입니다.내 말은, 코드를 평가할 때의 어려움을 상상해보세요. else 정확히는 반대가 아니다. if, 중첩된 위치 if외부에서 거부된 일부 사항을 허용할 수도 있습니다. if, 기타 등등

마지막으로, 주제를 실제로 다루기 위해 우리는 Algol을 제외한 본질적으로 모든 초기 언어가 처음에 해당 버전에 따라 단일 진술만을 만들었다는 점에 유의해야 합니다. if-then-else.따라서 조건부 차단을 수행하는 유일한 방법은 다음과 같습니다. goto 역 조건을 사용하여 주위에 있습니다.미친, 알아요. 하지만 오래된 사양을 읽었습니다.최초의 컴퓨터는 바이너리 기계 코드로 프로그래밍되었기 때문에 어떤 종류의 HLL이라도 생명의 은인이었다는 것을 기억하세요.나는 그들이 정확히 어떤 HLL 기능을 가지고 있는지에 대해 너무 까다롭지 않은 것 같습니다.

내가 하나를 붙이곤 했던 모든 것을 말한 후에 goto 내가 작성한 모든 프로그램에 "그냥 순수주의자들을 괴롭히려고".

프로그래머에게 GOTO 문 사용을 거부하는 것은 목수에게 망치를 사용하지 말라고 말하는 것과 같습니다. 망치로 못을 박는 동안 벽이 손상될 수 있기 때문입니다.실제 프로그래머는 GOTO를 언제 어떻게 사용하는지 알고 있습니다.나는 소위 '구조화된 프로그램' 중 일부를 추적해 왔으며 GOTO를 사용하지 않기 위해 프로그래머를 쏴버릴 수도 있는 끔찍한 코드를 보았습니다.좋아요, 상대방을 옹호하기 위해 실제 스파게티 코드도 몇 번 봤습니다. 그 프로그래머도 총에 맞아야 합니다.

다음은 제가 찾은 작은 코드 예입니다.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------또는----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10

"이 링크에서 http://kerneltrap.org/node/553/2131"

아이러니하게도 goto를 제거하면 버그가 발생합니다.spinlock 호출이 생략되었습니다.

원본 논문은 "무조건적인 GOTO가 유해한 것으로 간주됨"으로 간주되어야 합니다.특히 조건부 기반 프로그래밍 형식을 옹호했습니다(if) 및 반복(while) 초기 코드에서 흔히 볼 수 있는 테스트 및 점프 대신 구성을 사용합니다. goto 적절한 제어 구조가 없는 일부 언어나 상황에서는 여전히 유용합니다.

내가 동의하는 유일한 장소에 대해 Goto ~할 수 있었다 오류를 처리해야 할 때 사용되며 오류가 발생하는 각 특정 지점에는 특별한 처리가 필요합니다.

예를 들어 리소스를 잡고 세마포어나 뮤텍스를 사용하는 경우 순서대로 가져와야 하며 항상 반대 방식으로 해제해야 합니다.

일부 코드에는 이러한 리소스를 확보하는 매우 이상한 패턴이 필요하며 교착 상태를 피하기 위해 이러한 리소스를 확보하고 해제하는 작업을 올바르게 처리하기 위해 쉽게 유지 관리되고 이해되는 제어 구조를 작성할 수는 없습니다.

goto 없이 올바르게 수행하는 것은 항상 가능하지만 이 경우와 다른 몇몇 경우에는 실제로 Goto가 주로 가독성과 유지 관리 측면에서 더 나은 솔루션입니다.

-아담

최신 GOTO 사용법 중 하나는 C# 컴파일러를 사용하여 항복 반환으로 정의된 열거자에 대한 상태 시스템을 만드는 것입니다.

GOTO는 프로그래머가 아닌 컴파일러가 사용해야 하는 것입니다.

C 및 C++(다른 범인 중에서)가 중단 및 계속이라는 레이블을 붙일 때까지 goto는 계속해서 역할을 수행합니다.

GOTO 자체가 사악하다면 컴파일러도 JMP를 생성하므로 사악할 것입니다.코드 블록으로 점프하는 것, 특히 포인터를 따라가는 것이 본질적으로 나쁜 것이라면 RETurn 명령도 나쁜 것입니다.오히려 악은 남용될 가능성이 있습니다.

때로는 이벤트에 대한 응답으로 각 개체가 복잡한 상태 순서를 따라야 하는 여러 개체를 추적해야 하는 앱을 작성해야 했지만 모든 것이 확실히 단일 스레드였습니다.의사 코드로 표현되는 경우 일반적인 상태 순서는 다음과 같습니다.

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

이것이 새로운 것은 아니라고 확신하지만 C(++)에서 처리한 방식은 일부 매크로를 정의하는 것이었습니다.

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

그런 다음 (상태가 초기에 0이라고 가정) 위의 구조화된 상태 머신이 구조화된 코드로 변합니다.

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

이에 대한 변형으로 CALL 및 RETURN이 있을 수 있으므로 일부 상태 머신은 다른 상태 머신의 서브루틴처럼 작동할 수 있습니다.

특이한가요?예.관리자 측에서 약간의 학습이 필요합니까?예.그 배움이 성과를 거두나요?그렇게 생각해요.블록으로 점프하는 GOTO 없이도 가능합니까?아니요.

동료/관리자가 코드 검토에서나 우연히 발견했을 때 그 사용에 의심의 여지가 없기 때문에 나는 그것을 피합니다.나는 그것이 용도가 있다고 생각하지만(예를 들어 오류 처리 사례), 그것에 대해 어떤 유형의 문제가 있는 다른 개발자와 충돌하게 될 것입니다.

그것은 가치가 없어.

실제로 나는 이 코드를 작성하는 더 나은(빠른) 방법을 문자 그대로 생각할 수 없었기 때문에 어쩔 수 없이 goto를 사용해야 했습니다.

나는 복잡한 객체를 가지고 있었고 그것에 대해 몇 가지 작업을 수행해야 했습니다.개체가 한 상태에 있으면 빠른 버전의 작업을 수행할 수 있고, 그렇지 않으면 느린 버전의 작업을 수행해야 했습니다.문제는 어떤 경우에는 느린 동작이 진행되는 와중에 이것이 빠른 동작으로 가능했음을 깨달을 수 있었다는 것이다.

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

이는 실시간 UI 코드의 속도가 중요한 부분이므로 솔직히 여기서 GOTO가 정당하다고 생각합니다.

휴고

goto를 사용할 수 있는 거의 모든 상황에서는 다른 구문을 사용하여 동일한 작업을 수행할 수 있습니다.Goto는 어쨌든 컴파일러에서 사용됩니다.

나는 개인적으로 그것을 명시적으로 사용하지 않으며, 그럴 필요도 없습니다.

내가 보지 못한 것 중 하나 어느 여기에 대한 대답은 'goto' 솔루션이 종종 더 효율적 자주 언급되는 구조화된 프로그래밍 솔루션 중 하나가 아닙니다.

다수의 루프 대신 'goto'를 사용하는 다중 중첩 루프 사례를 고려해보세요. if(breakVariable) 섹션이 분명히 더 효율적입니다."루프를 함수에 넣고 리턴을 사용하십시오"라는 솔루션은 종종 완전히 비합리적입니다.루프가 지역 변수를 사용하는 경우 이제 함수 매개 변수를 통해 모든 변수를 전달해야 하며 잠재적으로 이로 인해 발생하는 추가 문제를 처리해야 합니다.

이제 제가 꽤 자주 사용해 왔고 아마도 많은 언어에서 사용할 수 없는 try{} catch {} 구조를 담당했을 정도로 너무 흔한 정리 사례를 생각해 보세요.동일한 일을 달성하기 위해 필요한 확인 횟수와 추가 변수는 점프를 하기 위한 한두 가지 지침보다 훨씬 더 나쁩니다. 그리고 다시 말하지만, 추가 기능 솔루션은 전혀 해결책이 아닙니다.더 관리하기 쉽고 읽기 쉽다고 말할 수는 없습니다.

이제 코드 공간, 스택 사용 및 실행 시간은 많은 상황에서 많은 프로그래머에게 충분히 중요하지 않을 수 있지만 작업할 코드 공간이 2KB밖에 없는 임베디드 환경에 있는 경우 명확하게 정의된 명령을 피하기 위해 50바이트의 추가 명령이 필요합니다. 'goto'는 우스꽝스러울 뿐이며 많은 고급 프로그래머가 믿는 것처럼 드문 상황은 아닙니다.

'goto는 해롭다'는 진술은 비록 항상 지나친 일반화였음에도 불구하고 구조화된 프로그래밍으로 나아가는 데 매우 도움이 되었습니다.이 시점에서 우리 모두는 그것을 사용하는 데 주의할 만큼 충분히 들었습니다.그것이 분명히 업무에 적합한 도구라면 우리는 그것을 두려워할 필요가 없습니다.

깊게 중첩된 루프를 깨는 데 사용할 수 있지만 대부분의 경우 깊게 중첩된 루프 없이 코드를 더 깔끔하게 리팩토링할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top