GOTO는 여전히 해로운 것으로 간주됩니까? [닫은]


282

누구나 편집자에게 보낸 Dijkstra의 편지를 알고 있습니다 : 유해한 것으로 간주되는 진술 ( 여기서는 .html 스크립트 및 .pdf)로 이동 하십시오 . goto를 사용하여 유지 관리 할 수없는 거대한 코드를 생성 할 수는 있지만 최신 프로그래밍 언어로 유지 됩니다 . Scheme 의 고급 연속 제어 구조 조차도 정교한 goto로 설명 할 수 있습니다.

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

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


Dijkstra는 자신이 책임을지지 않은 제목을 후회했습니다. 의 끝에서 EWD1308 (또한 여기 .PDF) 그가 쓴 :

마지막으로 레코드에 대한 짧은 이야기. 1968 년 ACM의 커뮤니케이션은 " 유해한 것으로 간주되는 goto 성명서 "라는 제목으로 내 텍스트를 출판 했다. 제목은 템플릿이되어 내 명성의 초석이되었습니다. "Dijkstra는 유해한 것으로 간주"라는 제목을 포함하여 거의 모든 X에 대해 "X는 유해한 것으로 간주"라는 제목의 모든 기사를 볼 수 있습니다. 그러나 무슨 일이 있었습니까? 제목이 ' 고토 진술에 대한 사례 '인 논문을 제출했습니다 .", 출판 속도를 높이기 위해 편집자는"편집자에게 편지 "로 바뀌었고, 그 과정에서 그는 자신의 발명품에 새로운 제목을 부여했습니다! 편집자는 Niklaus Wirth였습니다.

Dijkstra의 주제와 일치하는이 주제에 대해 잘 알려진 고전 논문은 Donald E. Knuth의 Statements to go with Statements로 구성된 Structured Programming입니다 . 두 가지를 모두 읽으면 상황에 대한 맥락과 비 도움적인 주제에 대한 이해를 회복하는 데 도움이됩니다. 이 백서에서이 사례에 대한 Dijkstra의 의견이보고되었으며 더욱 강력합니다.

Donald E. Knuth : 저는 그러한 견해를 제시함으로써 Dijkstra의 아이디어에 대해 최근에 다음과 같은 글을 썼기 때문에 실제로 동의하지 않는 것은 아니라고 생각합니다. ] 문으로 이동합니다. 내가 프로그래밍의 개념 문제를 규율을 코딩 간단한 형태로, 하나의 속임수에 의해 해결 될 수 있다면 다른 사람이, 그것의 종교를하고 있다는 불편한 느낌이! "


1
C #의 goto는 Dijkstra가 이야기 한 goto와 동일하지 않습니다. Dijkstra가 이야기 한 이유가 바로 그 때문입니다. 현대 언어는 대체 제어 구조를 제공하기 때문에 고토는 당시에는 해롭지 만 덜 필요합니다. C # goto는 매우 제한적입니다.
jalf

25
명확성을 추가하면 고토가 좋습니다. 중첩 된 루프가 길면 "break"변수를 설정하고 나올 때까지 끊는 것보다 루프에서 나가는 것이 좋습니다.
simendsjo

28
4 심도에 중첩 루프가있는 경우 (좋은 것은 아닙니다) 임시 값을 설정해야합니다. 여기에 goto가 훨씬 더 명확하며 IDE는 goto가 어디에 있는지 쉽게 보여야합니다. 즉, goto의 사용은 드
물어야

7
나는 당신이 9 천개와 1 개의 쓰레드를 읽도록 제안한다 goto.
Matti Virkkunen

5
사용하는 것보다 분명히 한 가지 더 나쁜 점이 있습니다 goto. 구조화 된 프로그래밍 도구를 함께 해킹하여 구현하는 것 goto입니다.

답변:


184

다음 내용은 일반화입니다. 예외를 항소하는 것이 항상 가능하지만 일반적으로 (나의 경험과 겸손한 의견으로는) 위험의 가치가 없습니다.

  1. 메모리 주소 (GOTO 또는 원시 포인터)를 제한없이 사용하면 쉽게 피할 수있는 실수를 할 수있는 기회가 너무 많습니다.
  2. 코드에서 특정 "위치"에 도달하는 방법이 많을수록 시스템의 상태에 대한 확신이 줄어 듭니다. (아래 참조)
  3. 구조화 된 프로그래밍 IMHO는 "GOTO를 피하는 것"이 ​​아니라 코드의 구조를 데이터의 구조와 일치시키는 것에 관한 것입니다. 예를 들어, 반복적 인 데이터 구조 (예를 들어, 배열, 순차 파일 등)는 반복되는 코드 단위에 의해 자연스럽게 처리된다. 내장 구조 (예를 들어, 동안, 동안, 각, 등)를 가짐으로써 프로그래머는 동일한 진부한 코드 패턴을 반복하는 지루함을 피할 수 있습니다.
  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에 도달하는 다양한 방법 은 그 시점 ab그 시점 의 관계에 대해 확신하기 위해 훨씬 더 열심히 노력해야한다는 것을 의미합니다 . (실제로 일반적인 경우 결정 불가능합니다!)

포인트 4와 관련하여 코드에서 "어딘가에 가고있다"는 전체 개념은 은유 일뿐입니다. 전자와 광자 (폐열에 대한 것)를 제외하고 CPU 내부에는 실제로 "가는"것이 없습니다. 때때로 우리는 더 유용한 다른 하나에 대한 은유를 포기합니다. 나는 수십 년 전에 언어를 만나는 것을 기억합니다.

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

가상 머신에서 action-1 및 action-2를 라인 외부 매개 변수없는 루틴으로 컴파일 한 다음 조건의 부울 값을 사용하여 하나의 다른 인수를 사용하는 단일 2 개의 인수 VM opcode를 사용하여 가상 머신에서 구현되었습니다. 그 개념은 단순히 "여기에 가거나 거기에 가라"보다는 "지금 무엇을 부를 것인지 선택"이었습니다. 다시, 은유의 변화.


2
좋은 지적입니다. 고급 언어에서 goto는 실제로 아무 의미도 없습니다 (Java의 메소드 간 점프 고려). Haskell 함수는 단일 표현식으로 구성 될 수 있습니다. goto로 그 밖으로 뛰어보십시오!
기계 달팽이

2
포스트 스크립트는 포인트 4 예제와 같이 작동합니다.
luser droog

스몰 토크는 포인트 4 예제와 비슷하게 작동합니다. "유사하게" "프로 시추 럴 언어와 같은 것은 없다"는 의미입니다. : P 언어에는 흐름 제어가 없습니다. 모든 결정은 다형성 (polymorphism)을 통해 처리 (있습니다 truefalse다른 유형의 수 있습니다), 그리고 경우 / 다른 사람의 각 지점은 기본적으로 람다이다.
cHao

이것들은 타당한 점이지만, 결국 "고용 될 경우"가 얼마나 나쁜지를 되풀이합니다. Break, Continue, Exit, Return, Gosub, settimeout, global, include 등은 정신적으로 사물의 흐름을 추적해야하는 모든 현대 기술이며, 스파게티 코드를 생성하고 변수 상태의 불확실성을 생성하기 위해 건너 뛸 수 있습니다. 공평하게 말해서, 나는 goto를 직접 사용하는 것을 본 적이 없지만, 한두 번만 사용 된 것을 본 적이 있습니다. 그것은 항상 가장 좋은 것들이 사용되어야한다는 성명서를 말해줍니다.
Beejor

goto최신 프로그래밍 언어 (Go)로 stackoverflow.com/a/11065563/3309046 .
nishanths

245

XKCD의 GOTO 코믹

내 동료가 GOTO를 사용하는 유일한 이유는 당신이 구석 구석까지 자신을 프로그래밍 할 수있는 유일한 방법이라고 말했다. 다시 말해, 적절한 디자인을 미리하고 나중에 GOTO를 사용할 필요가 없습니다.

나는이 만화가 아름답게 "프로그램 흐름을 재구성하거나 작은 'GOTO'를 대신 사용할 수있다"고 설명했다. 디자인이 약한 경우 GOTO는 약한 방법입니다. Velociraptors는 약점을 먹습니다 .


41
GOTO는 임의의 한 지점에서 다른 임의의 지점으로 '점프'할 수 있습니다. 벨로시 랩터는 어디에서나 여기로 뛰어 올랐습니다!
rpattabi

29
모두가 당신이 또한 당신이 실행할 수 있기 전에 연결해야한다는 것을 알고 있기 때문에 나는 농담을 전혀 재미있게 찾지 않습니다.
Kinjal Dixit

31
당신의 동료는 틀 렸으며 분명히 크 누스의 논문을 읽지 않았습니다.
Jim Balter

27
나는 고토를 피하기 위해 몇 년 동안 난독 화되고 왜곡 된 코드의 끝을 보지 못했습니다. @jimmcKeeth, 위의 xkcd 만화는 goto가 약하다는 것을 입증하지 않습니다. 그것을 사용하는 것에 대한 히스테리를 재미있게 만들고 있습니다.

4
Delphi 라이브러리 소스 코드에는 goto 문이 포함되어 있습니다. 그리고 이것들은 goto의 적절한 사용법입니다.
David Heffernan

131

때때로 단일 함수 내에서 예외 처리에 대한 대안으로 GOTO를 사용하는 것이 유효합니다.

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

return;

err_cleanup:
...

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


29
나는 goto가 코드를 단순화하고 더 읽기 / 유지 가능하게 만들 수있는 합법적 인 사용 사례가 있지만, 어떤 종류의 goto-pobia가 떠 다니는 것처럼 보인다는 것에 동의한다.
Pop Catalin

48
@Bob : 로컬 변수를 정리하는 경우 err_cleanup 코드를 서브 루틴으로 옮기기가 어렵습니다.
Niki

4
내가 없었해서 사실, COM / VB6에서 그것을 사용 에는 이 대안이었다 때문이 아니라, 대안을. try / catch / finally로 요즘 나는 행복합니다.
Rui Craveiro 2016 년

10
@ user4891 관용적 인 C ++ 방식은 시도하지 않습니다 {} catch () {cleanup; } 정리해야하는 자원이 소멸자에서 수행되는 RAII입니다. 모든 생성자 / 소멸자는 정확히 하나의 리소스를 관리합니다.
David Stone

5
이것을 C없이 작성하는 방법은 두 가지가 있습니다. 둘 다 훨씬 짧습니다. if (f ()) if (g ()) if (h ()) 반환 성공; 대청소(); 반품 실패; 또는 : if (f () && g () && h ()) 반환 성공; 대청소(); 반품 실패;
LHP

124

goto를 사용하여 한 번만 기억할 수 있습니다. 나는 일련의 5 개의 중첩 된 루프를 가지고 있었고 특정 조건에 따라 초기부터 전체 구조를 깨뜨릴 수 있어야했습니다.

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

ENDOFLOOPS:

부울 브레이크 변수를 쉽게 선언하고 각 루프에 대한 조건부로 사용했지만 GOTO가 실용적이고 읽기 쉬운 것으로 결정했습니다.

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


83
"함수로 리팩토링하고 goto를 return :으로 대체") 차이점은 무엇입니까? 정말로 차이점은 무엇입니까? 돌아 가지 않습니까? 리턴은 또한 goto와 같은 구조화 된 흐름을 제동하며,이 경우 동일한 방식으로 수행합니다 (goto를 사물에 사용할 수있는 경우에도 해당)
Pop Catalin

38
많은 루프를 중첩하는 것은 일반적으로 코드 자체의 냄새입니다. 5 차원 배열 곱셈과 같이하지 않으면 내부 루프 중 일부를 더 작은 함수로 유용하게 추출 할 수없는 상황을 파악하기가 어렵습니다. 모든 경험 법칙과 마찬가지로 몇 가지 예외가 있다고 생각합니다.
Doug McClean

5
반품으로 바꾸는 것은 반품을 지원하는 언어를 사용하는 경우에만 작동합니다.
Loren Pechtel

31
@leppie : 반란을 일으켜 goto구조화 된 프로그래밍을 제공 한 세대 도 같은 이유로 초기 수익을 거부했습니다. 코드의 가독성, 프로그래머의 의도를 얼마나 명확하게 표현했는지에 달려 있습니다. 악성 키워드를 사용하지 않는 것 이외의 목적으로 기능을 만들면 응집력이 떨어집니다. 치료법은 질병보다 나쁩니다.
Mud

21
@ButtleButkus : 솔직히 말해서, 그것은 나쁘지 않더라도 나쁘다. 적어도를 사용하면 대상을 명시 적으로 지정할 goto수 있습니다 . 으로 (1) 목적지를 찾기 위해 루프 폐쇄를 계산해야합니다. (2) 루프 구조가 변경되면 목적지를 올바르게 유지하기 위해 해당 번호를 변경해야 할 수도 있습니다. 내가 피하려고한다면 , 이득은 그런 것들을 수동으로 추적 할 필요가 없어야합니다. break 5;goto
cHao

93

우리는 이미이 토론을 했고 나는 입장을 고수 합니다 .

또한, 나는 고급 언어 구조를 " goto변장 중" 으로 묘사하는 사람들에게 지겨워한다. 왜냐하면 그들은 전혀 요점 얻지 못했기 때문이다 . 예를 들면 다음과 같습니다.

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

그건 말도 안되는 말이야 모든 제어 구조는 관점에서 구현 될 수 goto있지만이 관찰은 전혀 사소하고 쓸모가 없습니다. goto긍정적 인 효과로 인해 유해한 것으로 간주되지는 않지만 부정적인 결과로 인해 구조화 된 프로그래밍에 의해 제거되었습니다.

마찬가지로“GOTO는 도구이며 모든 도구가 사용하고 남용 할 수 있습니다”라는 문구가 완전히 표시되지 않습니다. 현대의 건설 노동자는 바위를 사용하지 않고“도구”라고 주장하지 않습니다. 바위는 망치로 대체되었습니다. goto제어 구조로 대체되었습니다. 만약 건설 노동자가 망치없이 야생에서 좌초된다면, 그는 대신 바위를 사용할 것입니다. 프로그래머가 기능 X가없는 열등한 프로그래밍 언어를 사용해야하는 경우에는 물론 goto대신 사용해야 합니다. 그러나 적절한 언어 기능 대신 다른 곳에서 사용하면 언어를 제대로 이해하지 못하고 잘못 사용합니다. 정말 간단합니다.


82
물론, 바위를 올바르게 사용하는 것은 망치가 아닙니다. 올바른 용도 중 하나는 연삭 또는 다른 도구를 연마하는 것입니다. 제대로 사용하지 않으면 바위가 적더라도 좋은 도구입니다. 적절한 사용법을 찾아야합니다. goto도 마찬가지입니다.
Kibbee

10
그렇다면 Goto의 올바른 사용법은 무엇입니까? 상상할 수있는 모든 경우에 더 적합한 다른 도구가 있습니다. 그리고 심지어 숫돌 실제로 그들은 여전히 만든 경우에도, 현재 첨단 도구로 대체 바위. 원료와 공구 사이에는 큰 차이가 있습니다.
Konrad Rudolph

8
@jalf : 고토 가장 확실 하지 C #으로 존재합니다. stackoverflow.com/questions/359436/…
Jon Skeet

51
많은 사람들이이 게시물을 승인 한 것에 대해 실망했습니다. 귀하의 게시물은 실제로 어떤 로직을 수행하고 있는지 질문하지 않았기 때문에 효과적이었습니다. 따라서 귀하의 오류를 알아 차리지 못했습니다. "모든 상황에서 goto를위한 우수한 도구가 있으므로 gotos를 절대 사용해서는 안됩니다." 이것은 논리적 인 조건이며, 따라서 전체 게시물은 본질적으로 "모든 상황에서 우수한 도구가 있다는 것을 어떻게 알 수 있습니까?"라는 질문을 본질적으로 요구하고 있습니다.
스타일로 코딩하기

10
@ 코딩 : 아니요, 게시의 요점을 완전히 놓쳤습니다. 그것은 고립되고 완전한 논쟁이 아닌 리 포스 테스 였다 . 나는 단지“for”라는 주요 논증의 오류를 지적했다 goto. 내가 goto그 자체 에 반대하는 주장을하지 않는 한 당신은 옳습니다 – 나는 의도하지 않았습니다 – 그래서 의문의 여지가 없습니다.
Konrad Rudolph

91

Goto는 그것을 위해 프로그램에 포함시킬 것들의 목록이 매우 낮습니다. 그렇다고 받아 들일 수없는 것은 아닙니다.

Goto는 상태 머신에 적합 할 수 있습니다. 루프의 switch 문은 (일반적으로 중요한 순서대로) 다음과 같습니다. (a) 실제로 제어 흐름을 나타내지 않음, (b) 못생긴, (c) 언어 및 컴파일러에 따라 잠재적으로 비효율적입니다. 따라서 상태 당 하나의 함수를 작성하고 "return NEXT_STATE;"와 같은 작업을 수행하게됩니다. 심지어 고토처럼 보입니다.

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

드문 경우에 알고리즘이 더 구체적인 제어 흐름 (루프, 조건부, 그렇지 않은 것)이 아닌 제한된 허용 가능한 전환 세트 (고 토스)로 연결된 일련의 노드 (상태)를 통한 경로로 가장 이해하기 쉬운 경우 ), 코드에서 명시 적이어야합니다. 그리고 당신은 예쁜 다이어그램을 그려야합니다.

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

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

나쁜 코드를 작성하는 것이 가장 어려운 언어는 없었습니다. -도널드 크 누스

C에서 고토를 복용하는 것이 쉬워 C.에서 사실 좋은 코드를 작성하지 것이다, 오히려 C가되는 시점을 놓칠 되어 영광 어셈블러 언어의 역할을 할 수 있어야한다.

다음은 "유해한 것으로 간주되는 포인터", "유해한 것으로 간주되는 덕 타이핑"입니다. 그러면 안전하지 않은 프로그래밍 구조를 제거 할 때 누가 당신을 변호해야합니까? 뭐라고?


14
Personaly, 이것은 내가 수표를 준 의견입니다. 독자들에게 지적하고 싶은 한 가지는 "국가 기계"라는 말에는 어휘 분석기와 같은 일상적인 것들이 포함된다는 것입니다. lex somtime의 출력을 확인하십시오. 고 토스로 가득합니다.
TED

2
루프 (또는 이벤트 핸들러) 내부에서 switch 문을 사용하여 상태 머신을 올바르게 수행 할 수 있습니다. jmp 또는 goto를 사용하지 않고도 많은 상태 머신을 수행했습니다.
Scott Whitlock

11
+1 상태 머신의 화살표는 다른 제어 구조보다 '가까운'위치에 매핑됩니다. 물론, 다른 문제에 대해 잠시 대신 여러 개의 gotos를 사용할 수있는 것처럼 루프 내부에 스위치를 사용할 수 있지만 일반적으로 아이디어입니다. 이것이이 토론의 요점입니다.
Edmund

2
마지막 단락에서 인용해도 될까요?
Chris Lutz

2
그리고 2013 년에 우리는 이미 "유해한 것으로 간주되는 포인터"단계에 도달했습니다.
Isiah Meadows

68

에서 리눅스 : 커널 코드에서 고토를 사용하여 커널 트랩에, 리누스 토발즈 (Linus Torvalds)와 리눅스 코드 GOTOs의 사용에 대한 "새로운 남자"와 토론이있다. 거기에 아주 좋은 점이 있으며 Linus는 일반적인 오만을 입었습니다. :)

일부 구절 :

Linus : "아니, 당신은 Niklaus Wirth가 실제로 자신이 무슨 말을하는지 알고 있다고 생각하는 CS 사람들에 의해 세뇌를당했습니다. 그는 몰랐습니다.

-

Linus : "고토는 괜찮고, 많은 양의 들여 쓰기보다 더 읽기 쉽다고 생각합니다."

-

Linus : "물론 레이블을 설명 할 수없는 파스칼과 같은 어리석은 언어에서는 goto가 잘못 될 수 있습니다."


9
좋은 지적 이네요? 그들은 다른 언어가 아닌 언어로의 사용에 대해 논의하고 있습니다. 어셈블리로 프로그래밍 할 때는 모든 분기와 점프 가 진행 됩니다. 그리고 C는 "휴대용 어셈블리 언어"였습니다. 또한, 당신이 인용 한 구절 은 그가 고토가 좋다고 생각 하는지대해서는 아무 말도 하지 않습니다 .
jalf

5
와. 읽는 것이 실망 스럽다. Linus Torvalds와 같은 큰 OS 사용자가 그렇게 말하는 것보다 더 잘 알고 있다고 생각할 것입니다. Pascal (현대 Object 버전이 아닌 구식 pascal)은 68k 기간 동안 Mac OS Classic이 작성되었으며 당시 가장 진보 된 운영 체제였습니다.
메이슨 휠러

6
@mason Classic Mac OS에는 일부 파스칼 라이브러리가 있었지만 (최종 맥에서는 파스칼 런타임이 너무 많은 메모리를 차지했습니다) 코어 코드의 대부분은 어셈블러, 특히 그래픽 및 UI 루틴으로 작성되었습니다.
Jim Dovey

30
Linus는 출구 상태를 처리하기 위해 goto에 대해 (명백하게, Rik van Riel과 같이) 주장 만하며, C의 대체 구조가 대신 사용될 경우의 복잡성에 따라 그렇게합니다.
Charles Stewart

21
IMHO Linus가이 문제에 맞습니다. 그의 요점은 예외 처리와 비슷한 것을 구현해야하는 C로 작성된 커널 코드는 goto를 사용하여 가장 명확하고 간단하게 작성된다는 것입니다. 관용구는 goto cleanup_and_exit우리가 가진 것을 지금 남아 고토의 몇 가지 "좋은"용도 중 하나입니다 for, while그리고 if우리의 제어 흐름을 관리 할 수 있습니다. 또한보십시오 : programmers.stackexchange.com/a/154980
steveha

50

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

나는 gotoC에서 의 위험 이 크게 과장 되었다고 믿는다 . 원래의 goto주장은 초보자가 다음과 같이 스파게티 코드를 작성하는 구식 BASIC과 같은 언어 시대에 다시 발생했습니다.

3420 IF A > 2 THEN GOTO 1430

여기 리누스의 적절한 사용을 설명 goto: http://www.kernel.org/doc/Documentation/CodingStyle (7 장).


7
BASIC을 처음 사용할 때는 GOTO nnnn과 GOSUB mmmm을 대체 할 방법이 없었습니다. 구조화 된 구조는 나중에 추가되었습니다.
Jonathan Leffler

7
당신은 요점을 잃어 버렸습니다 ... 그때조차도 스파게티를 쓸 필요가 없었습니다 ... GOTOs는 항상 훈련 된 방식으로 사용될 수 있습니다
JoelFan

또한 setjmp/ 의 동작 longjmp이 동일한 범위 내의 다른 장소에서 범위 내의 한 지점으로 점프하는 수단으로 사용될 때만 지정 되었다는 점에 주목할 가치가 있습니다. 제어 setjmp가 실행되는 범위를 벗어나 longjmp면 생성 된 구조 에서 사용하려고하면 setjmp정의되지 않은 동작이 발생합니다.
supercat

9
BASIC의 일부 버전에서는 가능합니다 GOTO A * 40 + B * 200 + 30. 이것이 매우 편리하고 위험한 방법을 보는 것은 어렵지 않습니다.
Jon Hanna

1
@Hjulle은 표현식을 계산 한 다음 해당 번호의 코드 줄로 이동합니다 (명확한 줄 번호는 대부분의 초기 방언의 요구 사항이었습니다). ZX Spectrum Basic은 그것을 받아 들일 것입니다
Jon Hanna

48

오늘날 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단일 서브 루틴 내에서 의 s 의 제한에 의존 할 수 없었기 때문에 ( GOSUB...RETURN그러한 약한 서브 루틴 개념 이기 때문에 ) 이것들 GOTO어디든수 있습니다 . 분명히 이것은 제어 흐름을 따르기가 어려워졌습니다.

이것이 바로 반 GOTO운동이 시작된 곳 입니다.


주목해야 할 또 다른 사항은 루프에 코드가 거의없는 경우 코드를 작성하는 기본 방법은 420 if (rare_condition) then 3000// 430 and onward: rest of loop and other main-line code// 3000 [code for rare condition]// 3230 goto 430입니다. 일반적인 메인 라인에서 분기 또는 점프를 피하는 방식으로 코드를 작성하지만 추적하기가 어렵습니다. 일부 분기가 예를 들어 +/- 128 바이트로 제한되고 때로는 상보 적 쌍이없는 경우 (예 : "cjne"은 존재하지만 "cje"는 제외) 어셈블리 코드에서 분기 방지는 더 나쁠 수 있습니다.
supercat

나는 한 번 128 사이클마다 한 번씩 실행되는 인터럽트가있는 8x51 용 코드를 작성했습니다. ISR의 일반적인 경우에 소비되는 모든 추가주기는 메인 라인 코드의 실행 속도를 1 % 이상 줄입니다 (128 개 중 약 90 개의주기가 일반적으로 메인 라인에서 사용 가능하다고 생각합니다). 분기 및 전도 사례 모두에서). 코드에는 두 가지 비교가있었습니다. 하나는 일반적으로 동일하게보고됩니다. 다른 하나는 같지 않습니다. 두 경우 모두, 드문 경우 코드는 128 바이트 이상 떨어져있었습니다. 그래서 ...
supercat

cjne r0,expected_value,first_comp_springboard/.../ cjne r1,unexpected_value,second_comp_fallthrough//`ajmp second_comp_target` // first_comp_springboard: ajmp first_comp_target// second_comp_fallthrough: .... 아주 좋은 코딩 패턴은 아니지만 개별 사이클이 중요 할 때 그러한 일을합니다. 물론, 1960 년대에, 오늘날의 CPU는 종종 이상한 최적화가 필요하고 적시 컴파일 시스템이 존재하지 않는 CPU에 대해 최적화 코드를 적용 할 수 있기 때문에 이러한 최적화 수준은 오늘날보다 중요했습니다. 문제의 코드가 작성되었습니다.
supercat

34

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);

이제 기능적으로이 코드는 똑같은 일을합니다. 실제로 컴파일러가 생성 한 코드는 거의 동일합니다. 그러나 프로그래머가 Nogoto (두려운 학문적 견책의 신) 를 달래려는 열의 에서이 프로그래머는 while루프가 나타내는 근본적인 관용구를 완전히 깨뜨 렸고 코드의 가독성을 실제로 평가했습니다. 이것은 좋지 않습니다.

따라서 이야기의 도덕은 goto 사용을 피하기 위해 정말로 바보 같은 것에 의지한다면 발견하지 마십시오.


1
나는 당신이 여기서 말하는 것에 동의하는 경향이 있지만, 그 break진술이 통제 구조 안에 있다는 사실은 그들이하는 일을 정확하게 명확하게합니다. 으로 goto예를 들어, 코드를 읽는 사람은 이론적으로, 실제로 제어 구조하기 전에 할 수있는 라벨을 찾을 수있는 코드를 살펴한다. 나는 구식 C에 대해 충분히 경험이 없어서 하나가 다른 것보다 확실히 낫다는 판단을 내릴 수는 없지만 어느 쪽이든 타협이 있습니다.
Vivian River

5
@DanielAllenLangdon : breaks가 루프 안에 있다는 사실 은 루프를 빠져 나간다는 것을 명확하게합니다 . 실제로는 정확히 루프가 없기 때문에 "정확히 그들이하는 일"이 아닙니다! 그것의 어떤 것도 반복 할 기회가 없지만, 끝날 때까지 분명하지 않습니다. "루프"가 두 번 이상 실행 되지 않는다는 사실은 제어 구조가 남용되고 있음을 의미합니다. 으로 goto예를 들어, 프로그래머는 말할 수있다 goto error_handler;. 더 명확하고 따르기가 훨씬 어렵습니다. (Ctrl + F, "error_handler :"를 사용하여 대상을 찾으십시오. "}"를 사용하여 시도해보십시오.)
cHao

1
한 번은 항공 교통 관제 시스템 에서 두 번째 예와 유사한 코드를 보았습니다 .
QED

30

Donald E. Knuth는 1992 년 CSLI의 "Literate Programming"책에서이 질문에 답변했습니다. 에 p. 17 " goto 문으로 구조화 된 프로그래밍 "(PDF) 에세이가 있습니다. 나는 그 기사가 다른 책에서도 출판되었을 것으로 생각합니다.

이 기사는 Dijkstra의 제안과 이것이 유효한 상황을 설명합니다. 그러나 그는 또한 구조화 된 루프만으로는 쉽게 재현 할 수없는 많은 카운터 예제 (문제 및 알고리즘)를 제공합니다.

이 기사에는 문제에 대한 완전한 설명, 기록, 예제 및 카운터 예제가 포함되어 있습니다.


25

도움이되는 것으로 간주됩니다.

1970 년대 프로그래머에게 "고토는 해로운 것으로 간주된다"는 말은 현대적인 제어 구조를 가진 새로운 프로그래밍 언어가 시도해 볼 가치가 있다고 말했다. 우리는 새로운 언어를 시도했습니다. 우리는 빨리 회심했다. 우리는 다시는 돌아 오지 않았습니다.

우리는 다시는 돌아 오지 않았지만, 만약 당신이 더 어리다면 처음에는 그곳에 가본 적이 없습니다.

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

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

그러나이 특정 슬로건 인 "고토는 해로운 것으로 간주했다"는 그 자체의 언데드 생활을 취했다.

학대를 당할 수 없습니까? 답 : 물론입니다. 그렇다면 무엇입니까? 실제로 모든 프로그래밍 요소 가 남용 될 있습니다. bool예를 들어 겸손 은 우리 중 일부가 믿고 싶어하는 것보다 더 자주 학대를받습니다.

대조적으로, 나는 1990 년 이래로 실제 단일 고토 ​​남용 사례를 만나는 것을 기억할 수 없습니다.

goto의 가장 큰 문제는 아마도 기술적 인 것이 아니라 사회적 일 것입니다. 많이 알지 못하는 프로그래머는 때때로 goto를 사용하지 않으면 스마트하게 들린다 고 생각하는 것 같습니다. 때때로 그러한 프로그래머를 만족시켜야 할 수도 있습니다. 그런 삶입니다.

오늘날 goto의 최악의 점은 충분하지 않다는 것입니다.


24

Jay Ballou가 대답을 추가하여 끌면 £ 0.02를 추가하겠습니다. Bruno Ranschaert가 아직 그렇게하지 않았다면 Knuth의 "GOTO 문으로 구조화 된 프로그래밍"기사를 언급했을 것입니다.

내가 보지 못한 것 중 하나는 정확히 공통적이지는 않지만 Fortran 교과서에서 가르치는 일종의 코드입니다. DO 루프의 확장 된 범위 및 오픈 코드 서브 루틴과 같은 것 (Fortran 77 또는 90이 아닌 Fortran II 또는 Fortran IV 또는 Fortran 66 임)을 기억하십시오. 구문 세부 사항이 정확하지 않을 가능성이 있지만 개념은 충분히 정확해야합니다. 각 경우의 스 니펫은 단일 기능 내에 있습니다.

Kernighan & Plauger가 쓴 훌륭하지만 날짜가 적힌 (그리고 인쇄되지 않은) 책 ' The Programming of Style of Style, 2nd Edn '에는 당시의 교과서 프로그래밍 (후기 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를 사용할 수 있습니다. Googling이 할당 한 goto 는 그것이 Fortran 95에서 삭제되었다고 알려줍니다. Dijkstra의 "GOTO로 간주되는 유해한"서한이나 기사를 통해 공개적으로 시작될 수있는 구조화 된 프로그래밍 혁명을위한 초크.

Fortran (및 대부분의 길가에 제대로 빠진 다른 언어)에서 수행 된 일에 대한 지식이 없으면, 새로 온 사람들이 Dijkstra가 다루고있는 문제의 범위를 이해하기 어렵습니다. 도대체 그 편지가 출판 된 후 10 년이되기 전까지는 프로그래밍을 시작하지 않았습니다.


2
goto'in the wild'를 사용하는 일부 코드의 예를 보려면 Wanted : Working Bose-Hibbard Sort Algorithm 에 1963 년에 출판 된 일부 (Algol 60) 코드가 표시됩니다. 원래 레이아웃은 최신 코딩 표준과 비교되지 않습니다. 명확하게 된 (들여진) 코드는 여전히 상당히 까다 롭습니다. goto이 문 수행은 이 (매우) 하드 알고리즘은 최대 무엇인지 이해 할 수 있습니다.
Jonathan Leffler

1
펀치 카드에 가까운 것을 경험하기에는 너무 어리기 때문에 재 펀칭 문제에 대해 읽는 것이 깨달았습니다. +1
Qix-MONICA WISTREATED

20

GOTO가 해로운 것으로 간주하는 것은 없습니다 .

GOTO는 도구이며 모든 도구로서 사용 및 남용 될 수 있습니다 .

그러나 프로그래밍 세계에는 많은 도구 가 사용되는 것보다 남용 되는 경향이 있으며 GOTO가 그 중 하나입니다. 델파이 의 WITH 문은 또 다른 것입니다.

개인적으로 나는 전형적인 코드 중 어느 것도 사용하지 않지만 보증 된 GOTOWITH 의 이상한 사용법을 사용 했으며 다른 솔루션에는 더 많은 코드가 포함되어 있습니다.

가장 좋은 해결책은 컴파일러가 키워드가 오염 되었음을 경고하고 경고문을 없애기 위해 명령문 주위에 pragma 지시문을 채워야한다는 것입니다.

아이들에게 가위로 달리지 말라고하는 것과 같습니다 . 가위는 나쁘지 않지만 일부 사용법은 건강을 유지하는 가장 좋은 방법은 아닙니다.


GOTO의 문제점은 현대 프로그래밍 언어에서 당연한 것으로 여겨지는 중요한 불변성을 깨뜨린다는 것입니다. 예를 들어, 함수를 호출하면 함수가 완료되면 정상적으로 또는 예외적 인 스택 해제를 통해 호출자에게 제어권을 반환한다고 가정합니다. 이 함수가 GOTO를 잘못 사용하면 더 이상 사실이 아닙니다. 이로 인해 코드를 추론하기가 매우 어렵습니다. GOTO의 오용을 조심스럽게 피하는 것만으로는 충분하지 않습니다. GOTO가 우리의 의존성에 의해 오용되는 경우에도 문제는 여전히 발생합니다 ...
Jonathan Hartley

... 따라서 함수 호출에 대해 추론 할 수 있는지 여부를 알기 위해서는 전이 종속성의 모든 소스 코드 소스를 검사하여 GOTO를 오용하지 않는지 확인해야합니다. 따라서 언어에 GOTO가 존재했기 때문에 코드를 완벽하게 사용하거나 절대 사용하지 않더라도 자신의 코드를 자신있게 추론 할 수 없게되었습니다. 이러한 이유로 GOTO는 단지 신중하게 사용되는 도구가 아닙니다. 그것은 체계적으로 깨져 있고 언어에서의 존재는 일방적으로 유해한 것으로 간주됩니다.
Jonathan Hartley

goto키워드가 C #에서 제거 된 경우에도 "jump here"라는 개념은 여전히 ​​IL에 존재합니다. goto 키워드없이 무한 루프를 쉽게 구성 할 수 있습니다. 호출 된 코드가 리턴된다는 보장이 부족하다고 생각하면 "코드에 대해 추론 할 수 없음"을 의미한다면, 우리는 그런 능력을 갖지 못했다고 말할 수 있습니다.
Lasse V. Karlsen

아, 통찰력이 우리의 잘못된 커뮤니케이션의 원인을 찾았습니다. gotoC에서 # 단어의 원래 의미에서 진짜 "고토"아니다. 함수 내에서만 점프 할 수있는 훨씬 약한 버전입니다. 내가 "goto"를 사용한다는 의미는 프로세스의 어느 곳에서나 점프 할 수있게 해줍니다. 따라서 C #에는 "goto"키워드가 있지만 실제 키워드는 없었습니다. 그렇습니다. 언어가 어셈블리로 컴파일 될 때와 같은 방식으로 IL 레벨에서 실제 고토를 사용할 수 있습니다. 그러나 프로그래머는 그것으로부터 보호되어 정상적인 상황에서는 사용할 수 없으므로 계산에 포함되지 않습니다.
Jonathan Hartley

덧붙여서, 내가 여기서 설명하는 의견은 원래 내 것이 아니라 1967 년 Dijkstra의 원본 논문의 핵심이라고 생각합니다. 그의 편집자 인 "Goto는 유해한 것으로 간주 됨"에 의해 제목이 지어졌습니다. 혁신적이고 통찰력이 있으며 보편적으로 받아 들여 졌기 때문에 정확히 몇 년이 걸렸습니다.
Jonathan Hartley

17

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


17

리눅스 커널에서 몇 가지 일을 시작했기 때문에 gotos는 한 번처럼 나를 귀찮게하지 않습니다. 처음에 나는 그들이 (커널 녀석들) 내 코드에 gotos를 추가 한 것을보고 무서웠다. 그 이후로 일부 제한된 상황에서 고 토스 사용에 익숙해졌으며 이제는 때때로 직접 사용할 것입니다. 일반적으로 함수의 여러 위치에서 동일한 정리 및 구제를 복제하는 대신 일종의 정리 및 구제를 수행하는 함수의 끝으로 이동하는 goto입니다. 그리고 일반적으로 다른 함수로 넘길 정도로 크지 않습니다. 예를 들어 일부 로컬 (k) malloc'ed 변수를 해제하는 것이 일반적인 경우입니다.

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

setjmp / longjmp는 그들의 장소를 가지고 있지만 그 장소는 당신이 방문하지 않을 것이지만 아주 오랫동안 한 번입니다.

편집 : 방금 코드를 보았습니다. 실제로는 longjmp가 아닌 사용하는 siglongjmp ()였습니다.


15

C로 VM을 작성하는 경우 (gcc)를 사용하여 다음과 같이 계산해야합니다.

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

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


표준이 아닌 유일한 문제입니까?
Calmarius

&&op_inc(왼쪽) &은 lvalue를 기대하지만 (오른쪽) &은 rvalue를 산출 하기 때문에 확실히 컴파일되지 않습니다 .
fredoverflow

7
@FredO : 특별한 GCC 운영자입니다. 그러나 나는 wtf의 진행 상황을 가장 확실하게 이해할 수 없기 때문에 상황의 혼란을 제외 하고는이 코드를 거부 할 것입니다.
강아지

이것은 goto와 암호 코드의 사용을 정당화하는 하나의 예 (속도에 대한 최대 최적화)이지만 최적화의 필요성, 대략적인 작동 방식 및 최상의 라인 별 설명을 위해 여전히 주석을 달아야합니다 할 수있는 의견. 따라서 여전히 실패하지만 유지 보수 프로그래머가 어둠에 빠져 있기 때문입니다. 그러나 나는 VM 예제를 좋아한다. 감사.
MicroservicesOnDDD

14

goto혼란스러운 메타 프로그래밍에 사용할 수 있기 때문에

Goto둘 다 높은 수준낮은 수준의 제어 표현, 그 결과 단지 대부분의 문제에 적합한 적절한 디자인 패턴이없는가.

그것은이다 낮은 수준의 고토가 원시적 작업이라는 의미에서 더 높은 같은 구현 뭔가 whileforeach또는 뭔가.

그건 높은 수준의 특정 방법으로 사용할 때 명확한 순서에서 실행이 중단 방식으로, 구조화 된 루프를 제외하고 그 코드를 취한다는 의미에서, 그리고 그것을 충분히 함께있는 논리의 조각으로 변경 goto의, 복 논리의 -bag는 동적으로 재 조립되고 있습니다.

따라서에 대한 찬성악한 면이 goto있습니다.

산문 측은 상향 포인팅 고토 완벽하게 합리적인 루프를 구현할 수 있고, 아래 방향 고토 완벽하게 합리적인 할 수 있다는 것 break또는 return. 물론 가난한 사람은 큰 그림을 얻기 위해 효과를 시뮬레이션 할 필요가 없기 때문에 실제 while, break또는 return훨씬 더 읽기 goto쉽습니다. 일반적으로 나쁜 생각입니다.

악마 측 루틴이 동안, 휴식, 또는 반환에 대한 고토를 사용하지만,라고하는 것에 대해 사용하지 않을 포함 스파게티 로직 . 이 경우 goto-happy 개발자는 goto의 미로에서 코드 조각을 작성하고 있으며 그것을 이해하는 유일한 방법은 전체적으로 정신적으로 전체를 시뮬레이션하는 것입니다. 내 말은, 코드 else가 정확하게 반대의 코드 가 아닌 if곳에서 중첩 된 ifs가 외부 if등에 의해 거부 된 것들을 허용 할 수있는 코드를 평가하는 문제를 상상해보십시오 .

마지막으로, 주제를 실제로 다루기 위해, 기본적으로 Algol을 제외한 모든 초기 언어는 단일 버전의 if-then-else. 따라서 조건부 블록을 수행하는 유일한 방법 goto은 역 조건부를 사용하는 것입니다. 미쳤지 만, 나는 오래된 사양을 읽었습니다. 첫 번째 컴퓨터는 바이너리 머신 코드로 프로그래밍되었으므로 어떤 종류의 HLL도 생명의 은인이라고 생각합니다. 나는 그들이 가지고있는 HLL 기능에 대해 너무 까다 롭지 않았다고 생각합니다.

goto내가 " 모든 순수 주의자들을 귀찮게하기 위해" 쓴 모든 프로그램에 하나를 붙이는 데 사용했던 모든 것을 말 했었다 .


4
순수 주의자를 귀찮게하여 +1! :-)
Lumi 2016

10

프로그래머에게 GOTO 문 사용을 거부하는 것은 목수에게 망치로 망치를 쓰지 말라고 망치로 쓰지 말라고 말하는 것과 같습니다. 실제 프로그래머는 GOTO 사용 방법과시기를 알고 있습니다. 나는 소위 '구조화 된 프로그램 (Structured Programs)'의 일부를 따랐다. 나는 프로그래머를 쏠 수있는 GOTO 사용을 피하기 위해 그러한 Horrid 코드를 보았다. 좋아, 다른 쪽을 방어하기 위해, 나는 진짜 스파게티 코드를 여러 번 보았습니다. 그 프로그래머들도 총에 맞아야합니다.

여기 내가 찾은 작은 코드 예제가 있습니다.

  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

3
CRT '정확합니까? (Y / N) : ': YORN ='Y '또는 YORN ='N '까지 YORN 입력; 등
joel.neely

5
사실, 그러나 더 중요한 것은 실제 프로그래머는 언제 사용 하지 않아야 하는지를goto 알고 왜 그런지 아는 것 입니다. $ programming_guru가 그렇게 말했기 때문에 금기 언어 구성을 피하는 것이 바로화물 컬트 프로그래밍의 정의입니다.
Piskvor는

1
좋은 비유입니다. 인쇄물을 손상시키지 않고 못을 박 으려면 망치를 제거하지 마십시오. 오히려 네일 펀치라는 간단한 도구를 사용합니다. 끝이 뾰족하고 끝이 뾰족한 금속 핀으로 손톱 머리와 단단히 결합됩니다 (이 도구는 손톱마다 크기가 다릅니다). 못 펀치의 다른 무딘 끝은 망치로 쳤다.
Kaz


7

원본 논문은 "무조건 GOTO는 유해한 것으로 간주 됨"으로 생각해야합니다. 특히 초기 코드에 공통적 인 테스트 및 점프보다는 조건부 ( if) 및 반복 ( while) 구문을 기반으로하는 프로그래밍 형식을 옹호했습니다 . goto적절한 제어 구조가없는 일부 언어 또는 환경에서 여전히 유용합니다.


6

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

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

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

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

-아담


5

현대의 GOTO 사용법 중 하나는 C # 컴파일러에서 수익률 반환으로 정의 된 열거자를위한 상태 머신을 만드는 것입니다.

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


5
누가 정확하게 컴파일러를 생성한다고 생각하십니까?
tloach

10
물론 컴파일러!
세 이티

3
그는 "GOTO는 컴파일러가 생성 한 코드에서만 사용해야하는 것"을 의미한다고 생각합니다.
Simon Buchan

에 대한 경우 goto입니다. 우리가 사용할 수있는 경우 goto열거를 구현하기 위해 손으로 코딩 된 상태 머신에서, 우리는 그냥 사용할 수 있습니다 yield대신.
Jon Hanna

goto 문으로 전환하기 위해 기본 케이스 컴파일을 사용하여 많은 문자열을 켭니다 (if-else로 컴파일하지 않도록).
M.kazem Akhgary 23 '12

5

C와 C ++ (다른 범인들 사이에서)이 중단을 표시하고 계속할 때까지 goto는 계속 역할을 수행 할 것입니다.


따라서 분류 또는 중단이라는 레이블은 goto와 어떻게 다릅니 까?
Matthew Whited

3
제어 흐름에서 완전히 임의의 점프를 허용하지 않습니다.
DrPizza

5

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없이 할 수 있습니까? 아니.


1
두려움에서 언어 기능을 피하는 것은 뇌 손상의 징후입니다. 이것은 또한 지옥보다 더 근사하다.
벡터 Gorgoth

4

동료 / 관리자가 의심 할 여지없이 코드 검토 또는 넘어 질 때 사용에 의문을 제기하기 때문에 그것을 피합니다. 내가 사용한다고 생각하지만 (예 : 오류 처리 사례)-어떤 종류의 문제가있는 다른 개발자를 무시할 것입니다.

그것은 가치가 없어.


C #의 Try ... Catch 블록에 대한 좋은 점은 예외가 예외 처리기까지 버블 링 될 때 스택 및 기타 할당 된 리소스 (스택 풀기라고 함)를 정리하는 것입니다. 이렇게하면 Try ... Catch가 Goto보다 훨씬 우수하므로 Try ... Catch가 있으면 사용하십시오.
Scott Whitlock

더 나은 해결책이 있습니다 .'do {...} while (0); '에서 튀어 나오려는 코드 덩어리를 감싸십시오. 고리. 그렇게하면 try / catch 루프의 오버 헤드없이 goto와 같은 방식으로 뛰어 뛸 수 있습니다 (C #에 대해서는 모르겠지만 C ++에서는 try가 저렴하고 catch가 비싸므로 비용이 많이 드는 것 같습니다. 간단한 점프만으로도 예외를 던질 수 있습니다.
Jim Dovey

10
짐, 그 문제는 고토를 얻는 바보 같은 길에 불과하다는 것입니다.
스타일로 코딩하기

4

실제로이 코드를 작성하는 더 나은 (빠른) 방법을 생각할 수 없기 때문에 실제로 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 방식은 fast_calculations 함수를 사용하는 것인데, 이로 인해 약간의 오버 헤드가 발생합니다. 아마도 대부분의 상황에서 눈에 띄지 않을 것입니다. 그러나 당신이 말한 것처럼 이것은 속도가 중요합니다.
Kyle Cronin

1
그다지 놀라운 일은 아닙니다. 절대적으로 미묘한 성능 지침을 가진 모든 코드는 항상 거의 모든 모범 사례를 위반합니다. 모범 사례는 10 밀리 초 이상을 압축하거나 5 바이트 더 많은 램을 절약하기위한 것이 아니라 접근성 및 유지 관리 성을위한 것입니다.
Jonathon

1
@JonathonWisnoski, goto의 합법적 인 사용은 쥐의 변수 중첩으로 방해하는 엄청난 양의 spagetti 코드를 제거하여 우리가 가고있는 곳을 추적합니다.
vonbrand

4

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

나는 개인적으로 그것을 절대로 사용하지 않으며, 절대로 필요하지 않습니다.


4

나는에서 볼 수없는 한 것은 어떤 여기에 대한 답변은 '고토'솔루션은 종종 있다는 것입니다 보다 효율적으로 자주 언급 구조화 된 프로그래밍 솔루션보다.

많은 if(breakVariable)섹션 대신 'goto'를 사용하는 것이 분명히 더 효율적인 많은 중첩 루프 사례를 고려하십시오 . 솔루션 "루프를 함수에 넣고 리턴을 사용하십시오"는 종종 불합리합니다. 루프가 로컬 변수를 사용하고있을 가능성이있는 경우 이제 함수 변수를 통해 모든 변수를 전달하여 잠재적으로 그로 인해 발생하는 추가 두통을 처리해야합니다.

이제 필자가 자주 사용했던 정리 사례를 생각해 보자. 많은 언어에서는 사용할 수없는 try {} catch {} 구조에 대해 책임이있는 것으로 추정된다. 동일한 작업을 수행하는 데 필요한 검사 및 추가 변수의 수는 점프를 수행하는 하나 또는 두 개의 명령보다 훨씬 나쁘며 추가 기능 솔루션은 전혀 솔루션이 아닙니다. 더 관리하기 쉽고 읽기 쉽다고 말할 수 없습니다.

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

'고토는 해롭다'는 말은 구조화 프로그래밍이 항상 과도하게 생성 되었음에도 불구하고 구조화 된 프로그래밍으로 나아가는 데 매우 도움이되었습니다. 이 시점에서 우리는 그것을 사용하는 것에 대해 조심해야한다고 들었습니다. 그것이 작업에 가장 적합한 도구 일 때, 우리는 그것을 두려워 할 필요가 없습니다.


3

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.