298

Dijkstra の編集者への手紙: go to ステートメントが有害であると見なされていることは誰もが認識しており(こちらの.html トランスクリプトとこちらの .pdf も)、その時以来、可能な限り goto ステートメントを避けるようにという手ごわいプッシュがありました。goto を使用して保守不可能な無秩序なコードを生成することは可能ですが、それでも最新のプログラミング言語に残っています。Schemeの高度な継続制御構造でさえ、洗練された goto として記述できます。

goto の使用が正当化されるのはどのような状況ですか? 避けるのがベストな時期は?

フォローアップの質問として: C は setjmp() と longjmp() の 1 組の関数を提供します。これらは、現在のスタック フレーム内だけでなく、任意の呼び出しフレーム内で goto する機能を提供します。これらは goto と同じくらい危険であると考えるべきですか? もっと危ない?


ダイクストラ自身は、彼が責任を負わなかったそのタイトルを後悔しました。EWD1308 (これも.pdf )の最後に、彼は次のように書いています。

最後に記録のための短い物語。1968 年、Communications of the ACM は、「 goto ステートメントは有害であると見なされた」というタイトルで私のテキストを公開しました。このタイトルは、テンプレートになることで私の名声の基礎となりました。「ダイクストラは有害と見なされます」というタイトルの記事を含め、ほぼすべての X について、「X は有害と見なされます」というタイトルの下にあらゆる種類の記事が表示されます。しかし、何が起こったのですか?私は「 goto文に対する訴訟」というタイトルで論文を提出していました」の発行を早めるために、編集者は「編集者への手紙」に変更し、その過程で彼は自分の発明の新しいタイトルを付けました! 編集者はニクラウス・ヴィルトでした.

このトピックに関するよく考えられた古典的な論文は、Dijkstra の論文と一致するように、Donald E. Knuth による構造化プログラミング with go to Statementsです。両方を読むことは、主題の文脈と非独断的な理解を再確立するのに役立ちます. この論文では、このケースに関するダイクストラの意見が報告されており、さらに強力です。

ドナルド E. クヌース:私は、そのような見解を提示することによって、実際にダイクストラの考えに激しく反対しているわけではないと信じています。彼は最近次のように書いているからです:まるでプログラミングの概念的な問題が 1 つのトリック、単純な形式のコーディング規律によって解決できるかのように、他の人がそれから宗教を作っているという不快な感じがあります!

4

49 に答える 49

255

XKCDのGOTOコミック

私の同僚は、GOTO を使用する唯一の理由は、自分自身を隅々までプログラムして、それが唯一の方法である場合であると言いました。つまり、事前に適切な設計を行えば、後で GOTO を使用する必要はありません。

この漫画は、「プログラムの流れを再構築したり、代わりに小さな「GOTO」を使用したりできる」ことを美しく示していると思いました。設計が弱い場合、GOTO は弱い方法です。 ヴェロキラプトルは弱いものを捕食します。

于 2008-09-05T20:06:00.637 に答える
191

以下のステートメントは一般化したものです。例外を認めることはいつでも可能ですが、通常は (私の経験と謙虚な意見では) リスクを冒す価値はありません。

  1. メモリ アドレス (GOTO または raw ポインター) を無制限に使用すると、簡単に回避できる間違いを犯す機会が多すぎます。
  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 オペコードを使用して、仮想マシンに実装されました。コンセプトは、「ここに行くかそこに行く」ではなく、単に「今何を呼び出すかを選択する」ことでした. 繰り返しますが、単なる比喩の変更です。

于 2008-09-09T16:34:07.100 に答える
139

単一の関数内で例外処理の代わりに GOTO を使用することが有効な場合があります。

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

return;

err_cleanup:
...

COM コードは、かなり頻繁にこのパターンに陥るようです。

于 2008-09-05T19:17:41.267 に答える
130

後藤を使ったのは一度だけ思い出せます。一連の5つのネストされたカウントループがあり、特定の条件に基づいて、構造全体を内側から早期に分割できる必要がありました。

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

ENDOFLOOPS:

ブールブレーク変数を簡単に宣言して、各ループの条件の一部として使用することもできましたが、この場合、GOTOは同じように実用的で読みやすいと判断しました。

ヴェロキラプトルは私を攻撃しませんでした。

于 2008-09-06T13:56:28.113 に答える
96

後藤は、そのためだけにプログラムに含めるべきもののリストの中で非常に少ないです。それが受け入れられないという意味ではありません。

Gotoはステートマシンに適しています。ループ内のswitchステートメントは(通常の重要度の順に)次のとおりです。(a)実際には制御フローを表していない、(b)醜い、(c)言語とコンパイラーによっては非効率的である可能性があります。したがって、状態ごとに1つの関数を記述し、「returnNEXT_STATE;」のようなことを行うことになります。後藤のようにも見えます。

確かに、ステートマシンを理解しやすい方法でコーディングすることは困難です。ただし、その難しさはgotoを使用することとは関係がなく、代替の制御構造を使用することで軽減することはできません。言語に「ステートマシン」構造がない限り。私のはしません。

より具体的な制御フロー(ループ、条件など)ではなく、限られた許容遷移(gotos)のセットによって接続されたノード(状態)のシーケンスを通るパスに関して、アルゴリズムが実際に最も理解しやすいまれなケースです。 )、それはコードで明示的にする必要があります。そして、あなたはきれいな図を描くべきです。

setjmp / longjmpは、例外または例外のような動作を実装するのに適しています。普遍的に賞賛されているわけではありませんが、例外は一般に「有効な」制御構造と見なされます。

setjmp / longjmpは、正しく使用するのが難しいという意味でgotoよりも「危険」です。わかりやすく気にしないでください。

悪いコードを書くのが最も難しい言語はこれまでになく、またこれからもありません。-ドナルド・クヌース。

Cからgotoを削除しても、Cで優れたコードを記述しやすくなることはありません。実際、Cが栄光のアセンブラー言語として機能できるはずであるという点を見逃してしまいます。

次に、「ポインタが有害であると見なされる」、次に「ダックタイピングが有害であると見なされる」になります。それでは、彼らがあなたの危険なプログラミング構造を奪うために来たとき、誰があなたを守るために残されますか?え?

于 2008-09-23T04:17:34.997 に答える
95

私たちはすでにこの議論をしていて、私は私の主張を支持します

さらに、私は、高級言語構造を「<code> goto in disgues」と表現する人々にうんざりしています。なぜなら、彼らは明らかにまったく意味をなしていないからです。例えば:

Schemeの高度な継続制御構造でさえ、洗練された後藤として説明することができます。

それは完全にナンセンスです。すべての制御構造は次の点で実装できますgotoが、この観察はまったく些細で役に立たないものです。gotoは、そのプラスの効果のために有害であるとは見なされていませんが、そのマイナスの結果のために、構造化プログラミングによって排除されています。

同様に、「GOTOはツールであり、すべてのツールと同様に、使用および悪用される可能性があります」と言うことは完全にオフマークです。現代の建設労働者は、岩を使って「道具だ」と主張することはありません。岩はハンマーに置き換えられました。goto制御構造に置き換えられました。建設作業員がハンマーなしで野生で立ち往生した場合、もちろん彼は代わりに岩を使用します。プログラマーが機能Xを持たない劣ったプログラミング言語を使用する必要がある場合は、もちろん、goto代わりに使用する必要があります。しかし、彼女が適切な言語機能の代わりにそれを他の場所で使用する場合、彼女は明らかに言語を正しく理解しておらず、それを誤って使用しています。それは本当にそれと同じくらい簡単です。

于 2008-09-06T14:00:07.777 に答える
70

Linuxの場合:カーネルトラップでのカーネルコードでのgotoの使用では、LinuxコードでのGOTOの使用について、LinusTorvaldsと「新人」との話し合いがあります。そこにはいくつかの非常に良い点があり、Linusはそのいつもの傲慢さに身を包んだ:)

いくつかの節:

ライナス:「いいえ、あなたはニクラウス・ヴィルトが実際に彼が話していることを知っていると思っていたCSの人々によって洗脳されました。彼は知りませんでした。彼には、気難しい手がかりがありません。」

-

Linus:「gotoは問題ないと思います。多くの場合、大量のインデントよりも読みやすくなっています。」

-

Linus:「もちろん、Pascalのような愚かな言語では、ラベルを説明することはできませんが、gotoの言語は悪いものになる可能性があります。」

于 2008-09-06T13:37:40.810 に答える
51

C では、goto潜在的なバグをローカライズする傾向がある現在の関数のスコープ内でのみ機能します。setjmpローカルでlongjmpはなく、複雑で、実装に依存しているため、はるかに危険です。ただし、実際には、それらはあまりにも曖昧で一般的ではないため、多くの問題を引き起こすことはありません。

in Cの危険性gotoは非常に誇張されていると思います。goto元の議論は、昔ながらの BASIC のような言語の時代にさかのぼって行われたことを思い出してください。そこでは、初心者は次のようなスパゲッティ コードを記述していました。

3420 IF A > 2 THEN GOTO 1430

ここで Linus は の適切な使用法を説明していますgoto: http://www.kernel.org/doc/Documentation/CodingStyle (第 7 章)。

于 2008-09-06T14:50:27.517 に答える
51

GOTO今日、 「構造化プログラミング」の人々が議論に勝利し、今日の言語には避けるべき十分な制御フロー構造があるため、このステートメントについて大したことを理解するのは困難GOTOです。

goto最新の C プログラムで の数を数えます。break次に、、、continueおよびreturnステートメントの数を追加します。ifさらに、 、elsewhile、の使用回数を加算してswitchくださいcase。これGOTOは、1968 年に Dijkstra が手紙を書いたときに FORTRAN または BASIC で書いていたとしたら、プログラムに含まれる s の数とほぼ同じです。

当時のプログラミング言語には、制御フローが欠けていました。たとえば、元のダートマス 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ループの場合、明示的にループを終了するGOTOIF...GOTO、最初に戻る必要がありました。

  • ありませんでしSELECT CASEた。を使用する必要がありましたON...GOTO

そのため、プログラムには多くGOTOs が含まれています。GOTOまた、単一のサブルーチン内への s の制限に依存することはできませんでした(サブルーチンGOSUB...RETURNの概念が非常に弱いため) 。明らかに、これにより制御フローを追跡するのが難しくなりました。GOTO

これが反GOTO運動の起源です。

于 2010-03-06T23:49:11.127 に答える
30

Donald E. Knuthは、1992年のCSLIの本「LiterateProgramming」でこの質問に答えました。p。17エッセイ「gotoステートメントを使用した構造化プログラミング」(PDF)があります。その記事は他の本にも掲載されていたのではないかと思います。

この記事では、ダイクストラの提案と、これが有効な状況について説明しています。しかし、彼はまた、構造化されたループだけでは簡単に再現できない多くの反例(問題とアルゴリズム)を示しています。

この記事には、問題の完全な説明、履歴、例、および反例が含まれています。

于 2008-09-06T13:47:18.620 に答える
28

後藤は役に立ったと考えています。

私がプログラミングを始めたのは 1975 年でした。1970 年代のプログラマーにとって、「goto は有害である」という言葉は多かれ少なかれ、最新の制御構造を備えた新しいプログラミング言語を試す価値があることを示していました。私たちは新しい言語を試しました。すぐに変換しました。私たちは二度と戻りませんでした。

私たちは戻ったことはありませんが、もしあなたが若いなら、そもそもそこに行ったことがありません.

さて、古代のプログラミング言語のバックグラウンドは、プログラマーの年齢の指標として以外にはあまり役に立たないかもしれません. それにもかかわらず、若いプログラマーにはこの背景が欠けているため、スローガンが導入された時点で「有害と見なされるように」というスローガンが意図した聴衆に伝えたメッセージを理解できなくなりました。

理解できないスローガンは、あまり啓発的ではありません。そのようなスローガンは忘れた方がよいでしょう。そのようなスローガンは役に立ちません。

しかし、この特定のスローガン「後藤は有害だと考えられている」は、独自のアンデッドの命を帯びています.

goto は悪用されないようにできますか? 答え: もちろんです。実質的にすべてのプログラミング要素悪用される可能性があります。たとえば、謙虚boolさは、私たちの一部が信じたいと思うよりも頻繁に虐待されています。

対照的に、私は 1990 年以降の goto 乱用の実際のインスタンスに一度も会ったことを覚えていません。

goto の最大の問題は、おそらく技術的なものではなく、社会的なものです。あまり詳しくないプログラマーは、goto を非推奨にするとスマートに聞こえると感じることがあるようです。時々、そのようなプログラマーを満足させる必要があるかもしれません。それが人生だ。

今日の goto で最悪なのは、十分に活用されていないことです。

于 2014-09-10T19:26:19.190 に答える
19

GOTO が有害と見なされるようなものはありません。

GOTO はツールであり、すべてのツールと同様に、使用および悪用される可能性があります。

ただし、プログラミングの世界には、使用されるよりも乱用される傾向のあるツールが数多くあり、GOTO はその 1 つです。DelphiのWITHステートメントは別のものです。

個人的には、典型的なコードではどちらも使用しませんが、保証されたGOTOWITHの両方の奇妙な使用法があり、代替ソリューションにはより多くのコードが含まれていました。

最良の解決策は、コンパイラがキーワードがtaintedであることを警告するだけであり、警告を取り除くためにステートメントの周りにいくつかのプラグマ ディレクティブを詰め込む必要があります。

はさみを持って走らないように子供たちに言っているようなものです。はさみは悪いものではありませんが、使い方によっては、健康を維持するための最良の方法ではないかもしれません。

于 2008-09-05T19:17:12.610 に答える
17

Linux カーネルでいくつかのことをやり始めて以来、goto は以前ほど気にならなくなりました。最初は、彼ら (カーネル担当者) が私のコードに goto を追加したのを見て、ちょっとぞっとしました。それ以来、いくつかの限られたコンテキストで goto を使用することに慣れてきたので、時々自分で使用するようになりました。通常、関数内のいくつかの場所で同じクリーンアップとベイルアウトを複製するのではなく、関数の最後にジャンプして何らかのクリーンアップとベイルアウトを行うのは goto です。そして通常、それは別の関数に引き渡すのに十分な大きさではありません-たとえば、ローカルで(k)mallocされた変数を解放することは典型的なケースです。

setjmp/longjmp を 1 回だけ使用するコードを作成しました。それは MIDI ドラム シーケンサー プログラムにありました。再生はすべてのユーザー インタラクションとは別のプロセスで行われ、再生プロセスでは UI プロセスと共有メモリを使用して、再生に必要な限られた情報を取得していました。ユーザーが再生を停止したい場合、再生プロセスは、ユーザーが停止したいときにたまたま実行していた場所の複雑な巻き戻しではなく、最初からやり直すために longjmp を実行するだけでした。それはうまく機能し、シンプルで、その場合、それに関連する問題やバグは一度もありませんでした.

setjmp/longjmp にはそれぞれの場所があります -- しかし、その場所はおそらく訪れることはなく、非常に長い間一度しか訪れない場所です。

編集:コードを見ただけです。私が実際に使用したのは siglongjmp() であり、longjmp ではありません (大したことではありませんが、siglongjmp が存在することさえ忘れていました)。

于 2009-04-23T03:53:02.520 に答える
16

メタプログラミングを混乱させるためgotoに使用できるため

Goto高レベルレベルの両方の制御式であり、その結果、ほとんどの問題に適した適切なデザインパターンがありません。

gotoは、または何かのようなより高いものを実装する基本的な操作であるという意味で、低レベルです。whileforeach

これは、特定の方法で使用すると、構造化されたループを除いて、中断されることなく明確なシーケンスで実行されるコードを取得し、十分なsを使用してグラブであるロジックの一部に変更するという意味で高レベルですgoto。 -動的に再構築されるロジックのバッグ。

ですから、には、無作法な側面と邪悪な側面がありgotoます。

上向きのgotoは完全に合理的なループを実装でき、下向きのgotoは完全に合理的なループを実行できbreakますreturn。もちろん、貧しい人間は全体像を把握するためにの効果をシミュレートする必要がないため、実際の、、whileまたはbreakreturnはるかに読みやすくなりgotoます。したがって、一般的には悪い考えです。

邪悪な側面には、 gotoをwhile、break、またはreturnの間使用せず、 spaghettiロジックと呼ばれるものに使用するルーチンが含まれます。この場合、goto-happy開発者は、gotoの迷路からコードを作成しています。それを理解する唯一の方法は、gotoが多数ある場合、全体として精神的にシミュレートすることです。これは非常に面倒な作業です。elseつまり、が正確にの逆ではないコードを評価する問題を想像してみてくださいif。ネストされたsは、外部などifによって拒否されたものを許可する可能性があります。if

最後に、この主題を実際にカバーするために、Algolを除く基本的にすべての初期の言語は、最初はのバージョンの対象となる単一のステートメントのみを作成したことに注意する必要がありif-then-elseます。したがって、条件付きブロックを実行する唯一の方法はgoto、逆条件付きを使用してブロックを囲むことでした。非常識です、私は知っています、しかし私はいくつかの古いスペックを読みました。最初のコンピューターはバイナリマシンコードでプログラムされていたので、どんな種類のHLLも命の恩人だったと思います。私は彼らが彼らが得たHLLの機能について正確にあまり気にしないことはなかったと思います。

goto私が書いたすべてのプログラムに1つを固執していたことをすべて言っても、「純粋主義者を困らせるためだけに」

于 2009-12-07T05:09:37.713 に答える
15

C で VM を作成している場合、(gcc の) 計算された goto を次のように使用することがわかります。

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

ループ内の従来のスイッチよりもはるかに高速に動作します。

于 2008-09-06T15:53:17.193 に答える
11

プログラマーに対して 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
于 2008-12-10T18:12:16.487 に答える
7

元の論文は、「無条件の GOTO は有害であると見なされる」と考えるべきです。特に、初期のコードに一般的なテスト アンド ジャンプではなく、条件 ( if) および反復 ( ) 構造に基づくプログラミング形式を提唱していました。適切な制御構造が存在しない一部の言語または状況では、依然として有用です。whilegoto

于 2008-09-05T19:08:25.190 に答える
7

Gotoを使用できる唯一の場所は、エラーに対処する必要があり、エラーが発生した特定のポイントごとに特別な処理が必要な場合です。

たとえば、リソースを取得してセマフォまたはミューテックスを使用している場合は、それらを順番に取得する必要があり、常に逆の方法で解放する必要があります。

一部のコードでは、これらのリソースを取得するための非常に奇妙なパターンが必要であり、これらのリソースの取得と解放の両方を正しく処理してデッドロックを回避するために、簡単に管理および理解できる制御構造を作成することはできません。

goto なしで正しく実行することは常に可能ですが、この場合と他のいくつかの場合は、主に読みやすさと保守性のために Goto の方が実際には優れたソリューションです。

-アダム

于 2008-09-05T19:45:19.140 に答える
5

このコードを書くためのより良い(より速い)方法を文字通り考えられなかったので、私は実際にgotoを使用することを余儀なくされました:

複雑なオブジェクトがあり、それに対して何らかの操作を行う必要がありました。オブジェクトが1つの状態にある場合は、操作のクイックバージョンを実行できます。それ以外の場合は、操作の低速バージョンを実行する必要がありました。場合によっては、低速動作の途中で、高速動作でこれが可能であることに気付くことができたということでした。

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はここで正当化されたと正直に思います。

ヒューゴ

于 2009-01-17T18:55:14.780 に答える
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なしでそれを行うことができますか?いいえ。

于 2008-11-27T13:49:26.850 に答える
5

ここでの回答から私が見たことのないことの 1 つ、「goto」ソリューションは、よく言及される構造化プログラミング ソリューションの 1 つよりも効率的であることが多いということです。

多数のネストされたループのケースを考えてみましょう。ここでは、一連のセクションの代わりに「goto」を使用するif(breakVariable)方が明らかに効率的です。「ループを関数に入れてリターンを使用する」という解決策は、多くの場合、まったく不合理です。ループがローカル変数を使用している可能性が高い場合、関数パラメーターを介してそれらをすべて渡す必要があり、そこから生じる余分な頭痛の種を処理する可能性があります。

ここで、クリーンアップのケースについて考えてみましょう。これは、私自身がかなり頻繁に使用しており、多くの言語では利用できない try{} catch {} 構造の原因であると思われるほど一般的です。同じことを達成するために必要なチェックの数と追加の変数は、ジャンプを行うための 1 つまたは 2 つの命令よりもはるかに悪いものであり、追加の関数ソリューションはまったくソリューションではありません。それがより扱いやすく、より読みやすいとは言えません。

現在、多くのプログラマーにとってコード空間、スタック使用量、および実行時間は多くの状況で十分に重要ではないかもしれませんが、作業するコード空間が 2KB しかない組み込み環境では、明確に定義された命令を避けるために 50 バイトの余分な命令が必要です。 「goto」はばかげているだけであり、これは多くの高レベルのプログラマーが信じているほどまれな状況ではありません。

「goto は有害である」というステートメントは、たとえそれが常に過度に一般化されていたとしても、構造化プログラミングに移行するのに非常に役立ちました。この時点で、私たちは皆、それを使用することに慎重になるのを十分に聞いています (当然のことです)。それが明らかに仕事に適したツールである場合、恐れる必要はありません。

于 2012-06-06T20:21:09.337 に答える
5

C と C++ (他の犯人の中でも) が中断と継続にラベルを付けるまで、goto は引き続き役割を果たします。

于 2008-09-06T14:44:38.230 に答える
5

最近の GOTO の 1 つの使用法は、C# コンパイラによるもので、yield return によって定義された列挙子のステート マシンを作成します。

GOTO は、プログラマーではなく、コンパイラーによって使用されるべきものです。

于 2008-09-05T19:20:45.317 に答える
4

gotoを使用できるほとんどすべての状況で、他の構成を使用して同じことを行うことができます。とにかく、Gotoはコンパイラによって使用されます。

私は個人的にそれを明示的に使用することは決してありません。

于 2010-06-17T07:41:44.683 に答える
4

同僚/マネージャーは、コードレビューで、または偶然見つけたときに、間違いなくその使用法に疑問を呈するため、私はそれを避けています. 用途があると思いますが(エラー処理の場合など)、何らかの問題を抱えている他の開発者と衝突するでしょう。

それはそれだけの価値はありません。

于 2008-09-05T20:16:46.040 に答える
3

はい、GOTOは依然として有害であると考えられています。GOTOの使用が有効である可能性があるまれな状況に陥るまでに、他の人の検証を必要としないように、自分のプログラミングスキルに十分な自信を持っている必要があります。GOTOで許可されているよりもスコープ内でさらに遠くにジャンプできるGOTOのような機能は、GOTOよりも危険であると見なす必要があります。

于 2009-04-23T03:37:27.643 に答える
3

C++ には、コンストラクタとデストラクタが含まれています。これにより、RAII (リソース割り当ては初期化) として知られるパターンが可能になります。基本的に、ローカル スタック変数を作成し、スタック変数を作成する行為によって、ファイルを開いたり、メモリを割り当てたり、ミューテックスをロックしたり、後で解放する必要があるリソースを取得したりします。

変数がスコープ外になると、デストラクタが実行され、リソースが解放されます。

Cにはこの機能がありません。しかし、多くの場合、関数の開始時にリソースを取得し、最後に解放する必要があります。

関数には、早期に戻る原因となるエラー条件が 1 つ以上ある可能性があります。リソース リリース コードを複製したくありません。解決策は、goto を使用することです。

例:

int
foo(const char *arg)
{
    char *argcopy = strdup(arg);

    if (!isvalid(argcopy))
        goto out1;

    FILE *myfile = fopen(argcopy, "r");
    if (myfile == NULL)
      goto out1;

    char bytes[10];
    if (fread(bytes, sizeof(bytes), 1, myfile) != sizeof(mybytes))
        goto out2;

    /* do some actual work */
    /* .... */
    /* end of actual work */

    out2:
    fclose(myfile);

    out1:
    free(argcopy);

    return 0;
 }
于 2012-06-02T04:08:06.860 に答える
3

GOTO はテーブル ソーのようなもので、適切な安全対策が講じられている場合に非常に役立ちます。

ほとんどの初心者はテーブルソーとGOTOの両方で指を失うため、有害だと思います。

フローを制御する唯一の方法である状況がいくつかありますが、それらの状況は回避できます。

于 2008-09-05T19:22:25.403 に答える
3

深くネストされたループから抜け出すために使用できますが、ほとんどの場合、深くネストされたループがなくても、コードをリファクタリングしてよりクリーンにすることができます。

于 2008-09-05T19:08:31.693 に答える
2

私が見たすべてのプラットフォームで、高レベルの制御構造が低レベルの goto (ジャンプ) として実装されています。たとえば、Java 仮想マシンには Jump バイト コードがありますが、if、else、while、for などには何もありません。

また、これらのコンパイラの一部は、単純な条件付きブロックのスパゲッティ コードを作成します。

あなたの質問に答えるために、goto は、それが有害であると信じている人々によって依然として有害であると考えられています。Goto を使用すると、構造化プログラミングの利点が失われやすくなります。

最終的には、それはあなたのプログラムです。したがって、あなたの決定。質問に自分で答えることができるまでは goto を使用しないことをお勧めしますが、特定の問題のコンテキストで使用することをお勧めします。

于 2008-09-23T18:29:12.797 に答える
2

ほとんどすべての状況で goto を避けるのが最善だと思いますが、例外もあります。たとえば、私が見た場所の 1 つである goto ステートメントは、他のはるかに複雑な方法と比較して洗練されたソリューションであり、インタープリターのテール コールの除去を実装しています。

于 2009-02-14T20:40:59.400 に答える
1

goto 自体が悪いわけではありません。同じロジックを別の方法でより明確に表現できる場合に goto を使用するのは悪いことです。コードを追跡するのが非常に難しくなり、メンテナンスが困難になります。例として、Bad Old Days の Basic のプログラムを見てみましょう。

私の意見では、C# のような現代的な言語では、通常の状況で goto が必要になることは決してないはずです。それを使用していることに気付いた場合、それは通常、ロジックを再考する必要があるという兆候です。通常のコード フロー ステートメントを使用して同じコードを表現するより明確な方法があることはほぼ確実です。

そうは言っても、gotoが非常に役立つ特別な目的があります (そして、goto を持たない言語にイライラすることもあります)。私は主に C で、複数レベルのループから抜け出すため、またはエラー処理のために使用します。C# には、これを行う必要がないことを意味する言語機能があると思います。(自動生成されたコードを生成するときにも非常に便利ですが、ほとんどの人が実際に遭遇することはありません。)

goto には別の問題もありますが、これは純粋に政治的なものです。多くの人が goto を嫌っており、たとえ正当化されたとしても、コードで使用すると問題が発生する可能性があります。これが割り当てコードである場合は、はい、書き直してください。そうしないと、マークダウンされる可能性があります。それ以外の場合は、次回そのセクションのメンテナンスが必要になるまでそのままにしておきます。

于 2010-08-09T21:08:43.230 に答える
1

私の意見では、「有害であること」は、何よりもカプセル化と状態の一貫性に関するものです。

多くのコードは、たとえ 'oo' コードであっても、これまでのスパゲッティ コードと同様に、乱雑な状態のカプセル化を行っています。

「有害と見なされる goto」の問題は、使用可能な唯一のフロー制御が return メソッドであるという印象を理解せずに機械的なルールだけを見るプログラマーを残し、非常に簡単に多くの状態を渡すことにつながることです。参考までに-そしてそれは状態のカプセル化の欠如に直接つながります.「gotoは有害と見なされた」まさにそれを取り除こうとしていました.

典型的な「OO」コードベースの制御の流れに従ってください。スパゲッティ コードがまだないことを教えてください....オブジェクトの関係がすぐにわからなくても、通常はラビオリ コードの実行パスは非常に単純です)。

または、別の言い方をすれば、すべてがサブルーチンであることを優先して goto を回避することは、各サブルーチンがローカル状態のみを変更し、そのサブルーチン (または少なくともそのオブジェクト) を介して以外は変更できない場合にのみ役立ちます。

于 2010-03-06T23:56:34.657 に答える
1

goto を使用すると、特にメンテナンスしにくい「スパゲッティ コード」を簡単に作成できます。従うべき最も重要なルールは、読みやすいコードを書くことですが、もちろんそれはプロジェクトの目標によって異なります。「ベスト プラクティス」として、goto を回避することをお勧めします。これは、何か間違ったことをしている可能性があることを示すため、極端なプログラミング タイプが「コードの匂い」と呼ぶものです。ループ中にブレークを使用することは、goto ではないことを除いて、goto と非常に似ていますが、コードが最適でない可能性があることを示しています。これが、本質的に別の名前による goto である、より現代的なプログラミングの抜け穴を見つけないことも重要であると私が信じている理由です。

于 2008-09-09T17:02:00.450 に答える
1

Basic(つまり、VB、VBScriptなど)とバッチファイルでのみ必要です。その後、エラー処理にのみ使用します。Basic では、「on error goto」のみを使用する傾向があります。バッチ ファイルでは、else コマンドがないため、これを使用する必要があります。次に、意味のあるラベルへの前方ジャンプとしてのみ使用します。

于 2008-09-05T20:42:52.420 に答える
1

プログラミング人生の初期に、チェーン内の一連の関数で構成されるプログラムを作成したことがあります。各関数は、成功条件と完了を指定して後続関数を呼び出します。

それは複数の深刻な問題を抱えた忌まわしいクラッジでした.

しかし、それは迅速に開発され、解決するように設計された限られた一連の問題に対してうまく機能し、プログラムのロジックとフローが明示的に示されました。これは、別のプロジェクトに含めるためにリファクタリングおよび拡張したときにうまく機能しました.

私の投票は、意味のあるときにそれを使用し、便利になったらすぐにリファクタリングすることです。

于 2008-11-28T03:04:21.817 に答える
1

goto にはいくつかの問題があります。1 つは、コードの流れがわかりにくいことです。中括弧のおかげで if ブロックは見やすくなっていますが、goto はそれを隠しています。また、while と if も本質的に goto ですが、コード内を行ったり来たりする理由を説明するのに役立ちます。自分でつなぎ合わせる必要がある通常の後藤を使用します。

演習として、フィボナッチ数列を計算するためのコードを書いてみてください。読み終わったときにどれだけ読みにくいかを確認してください。

そのコードで作業する場合は、いくつかの単体テストを作成して書き直すことをお勧めします。それ以外の場合は、そのままにしてください。

とはいえ、パフォーマンス上の理由から、goto を使用することが適切な場合もあります。

于 2010-08-09T20:53:18.410 に答える
1

最新のプログラミング言語の多くは、コンパイラを使用して GOTO の使用に制限を課しています。これにより、潜在的なリスクが削減されます。たとえば、C# では、GOTO を使用して、ループの外側からループの本体にジャンプすることはできません。制限事項はドキュメントに記載されています

これは、GOTO が以前よりも安全な場合があることを示す一例です。

場合によっては、GOTO の使用は、関数から早期に戻ることと同じです (つまり、ループから早期に抜け出すため)。しかし、良いフォームは主張することができます.

于 2009-10-24T03:54:25.797 に答える
0

ディスパッチ用に計算されたgotoは、非常に大きなswitchステートメントよりも理解しやすいことがよくあります。

エラーとコスレッドについては、setcontexまたはsetjmp(利用可能な場合)の方が「優れている」と思います。

于 2009-04-23T03:16:38.597 に答える
0

Java文字列クラスのソースコードでのジャンプの例:

int firstUpper;

/* Now check if there are any characters that need to be changed. */
scan: {
    for (firstUpper = 0 ; firstUpper < count; ) {
         char c = value[offset+firstUpper];
         if ((c >= Character.MIN_HIGH_SURROGATE) &&
                 (c <= Character.MAX_HIGH_SURROGATE)) {
             int supplChar = codePointAt(firstUpper);
             if (supplChar != Character.toLowerCase(supplChar)) {
                  break scan;
             }
             firstUpper += Character.charCount(supplChar);
         } else {
             if (c != Character.toLowerCase(c)) {
                  break scan;
             }
             firstUpper++;
         }
     }
     return this;
}
[... subsequent use of firstUpper ...]

これは、たとえば次のように、オーバーヘッドをほとんどかけずに書き直すことができます。

 int firstUpper = indexOfFirstUpper();
 if (firstUpper < 0) return this; 

現代語でも、実際にはgotoの使用は好きではありませんが、多くの場合は許容できると思いますが、このような低レベルのケースでは、私には見栄えがよくなります(そしてそれは単に終了するだけではありませんループ)。

宗教戦争を復活させるつもりはありません。

于 2013-02-19T15:13:46.210 に答える
0

基本的な考え方は、goto によって、意図しないことを行う自由が多すぎるということです。goto ステートメントに関連していないように見える場所でエラーが発生する可能性があるため、コードのメンテナンスがより困難になります。goto ステートメントが必要だと思うなら、それは間違いです :) 代わりに、コードの構成を再考する必要があります。これが、最新のプログラミング言語が、読み取り可能で保守可能なフロー制御構造と例外処理メカニズムを提供することに多大な努力を払ってきた理由です。

私もlassevkに反対するつもりです。goto は正しく使用されているというよりも乱用されているため、適切に設計された言語には適していないと思います。goto の「理想的な」用途であっても、より多くのコードを必要とする他の方法を使用することをお勧めします。

要約すると、はい、それはまだ有害であると考えられています.

于 2008-09-05T19:23:14.343 に答える
0

C ステート マシンを生成する場合は、GOTO を使用すると便利です。手書きのコードで GOTO を使用することは決してありません。「最新の」言語構造により、GOTO はまったく不要になります。

setjmp/longjmp コンストラクトは、特定の状況 (「真の」例外がない場合、または Chicken スキームのようなものを実装している場合) で役立ちますが、「通常の」プログラミングでは使用できません。

于 2009-05-18T22:40:40.063 に答える
0

完璧な世界では、GOTO は必要ありません。しかし、私たちは不完全な世界に住んでいます。夢見ることができるすべての制御構造を備えたコンパイラはありません。実際には存在しない制御構造を無視するよりも、GOTO を使用する方がよいと感じることがあります。

最も一般的な (一般的というわけではありませんが) のは、ループと半分の構造です。常に最初の部分を実行します。残りの部分を実行してから、戻って最初の部分をもう一度実行することもできます。もちろん、while ループ内でブール値フラグを使用して実装できますが、私の意見ではあまり明確ではないため、この回答は好きではありません。次のようなものが表示された場合:

loop:
  GetSomeData;
  if GotData then
     Begin
        ProcessTheData;
        StoreTheResult;
        Goto Loop;
     End;

私にはそれよりも明らかです

Repeat
  GetSomeData;
  Flag := GotData;
  if Flag then
    Begin
      ProcessTheData;
      StoreTheResult;
    End;
Until Not Flag;

そして時々ある

Function GotTheData;

Begin
  GetSomeData;
  Result := GotData;
End;

While GotTheData do
  Begin
    ProcessTheData;
    StoreTheResult;
  End;

実行可能な答えではなく、コードは明確であるべきだと固く信じています。コードが何をしているのかを説明するコメントを作成する必要がある場合は、コードをより明確にしてコメントを削除できないかどうかを検討します。

于 2009-10-24T03:46:26.930 に答える