1

ゲームが終わった後にもう一度プレイするかどうかをユーザーに尋ねるゲームを (C で) 書きたいとしましょう。

それを書くには2つの明白な方法があります。

初め:

int main(void)
{
    /* variable declarations and initializations */

    do { /* optionally multiple games */

        /* game code here */
        ........

        /* prompt user wheter he wants to play again */
        bool playagain = playagain();

    } while(playagain);

    .....
}

そして2番目:

int main(void)
{
    /* variable declarations and initializations */

game_start: /* optionally multiple games */

    /* game code here */
    ........

    /* prompt user wheter he wants to play again */
    bool playagain = playagain();

    if (playagain)
        goto game_start;

    .....
}

一般的に goto ステートメントを使用するのは悪い考えであることはわかっていますが、ここでは、コードをより明確にし、余分なインデント レベルから解放されると思います。

だから私の質問は、この特定の例は goto ステートメントを使用する適切な方法と見なされますか、それともとにかくそれを避けるべきですか?

4

6 に答える 6

6

これは良い例ではありません。K&R で良い例を見つけることができます。

C は、無限に悪用可能な goto ステートメントと、分岐先のラベルを提供します。正式には、goto ステートメントは必要ありません。実際には、ほとんどの場合、goto ステートメントがなくてもコードを簡単に記述できます。この本では goto を使用していません。それでも、後藤が居場所を見つける状況がいくつかあります。最も一般的なのは、一度に 2 つ以上のループから抜け出すなど、深くネストされた構造で処理を放棄することです。break ステートメントは、最も内側のループからのみ終了するため、直接使用することはできません。したがって:

         for ( ... )
              for ( ... ) {
                  ...
                  if (disaster)
                       goto error;
              }
         ...
   error:
         /* clean up the mess */

この構成は、エラー処理コードが重要で、複数の場所でエラーが発生する可能性がある場合に便利です。

于 2013-02-27T12:26:26.703 に答える
4

2番目の例では、基本的にループを作成していますが、既存のよく知られたループ構造の1つを使用する代わりに、if+gotoを使用します。

1つ目はもっとわかりやすいと思います。

于 2013-02-27T12:28:15.230 に答える
4

一般に、C を使用する人は、最初から C を使用する理由の 1 つとして「goto を必要としないこと」を考えているため、goto を嫌います。

goto を使用することに決めたら、先に進んで使用してください。ただし、一般的には、スタック オーバーフローについて質問するのに苦労する混乱するコードにつながります。

私は後藤を完全に避けます。

実際、私が goto を使用する唯一の理由は、アセンブリで記述する必要がある代入プログラムを作成する前に、C でプログラムを作成したい場合です (これは goto で視覚化するのがはるかに簡単です)。

いずれにせよ、「通常の」制御ステートメントよりも goto を使用する利点があると思われる場合は、先に進んでそれらを使用してください。ただし、ドラゴンがいることに注意してください。

于 2013-02-27T12:25:52.437 に答える
3

gotoについて40年以上議論した後も、世界中のすべてのプログラマーの間でコンセンサスは得られていません。

最も一般的な意見(?)は、gotoはまれなケースで問題ない場合があり、そのような場合は、下にジャンプするためにのみ使用する必要があるというものです。そのような例の1つは、テストされた複数のループまたはステートメントからの脱却です。もう1つの例は、関数の最後にあるラベルにジャンプするエラー処理、「貧乏人の例外処理」です。

gotoキーワードは常に他の言語メカニズムに置き換えることができるため、gotoは有害であると見なされ、いかなる状況でも使用されないものと考える人もいます。

個人的には、主に議論を避けるためにgotoを使用しないことを好みますが、代わりに、関数を備えた非常によく似た構造を使用します。

int main()
{
  while (play_a_game())
    ;
}

bool play_a_game (void)
{
  game();      

  return play_again();
}

同様に、gotoではなく、関数からのreturnステートメントを使用して、複数のループから抜け出すことができます。皮肉なことに、Cプログラマーが関数の戻り値やブレークステートメントに問題を起こすことはめったにありませんが、結果のマシンコードが同じである可能性が高い場合でも、gotoが表示されるたびに赤く表示されます。

経験則として、コードを読み取れなくするインデントの複数のレイヤーについて心配するときはいつでも、関数が常に解決策です。

于 2013-02-27T12:39:49.423 に答える
3

私はむしろ最初のものを使用したいと思います。それが最良の解決策である場合、使用gotoは悪くありません。たとえば、ネストされたスイッチが2つあり、最初のスイッチから2番目のスイッチに割り込みたい場合gotoは、追加の変数と条件の代わりに使用します。好きなコーディングスタイルを使用できますが、コードを簡単に台無しにする可能性があるため、通常は使用を避けます。しかし、それでもそれはあなたの選択です。

于 2013-02-27T12:31:40.683 に答える
0

私はそれを「ファイナライズ」の方法として使用することはめったにありません。

fun () {
  d = getDatabaseConnection(...);
  if (d) {
    f = fopen(...);
    if (f) {
       ...do things...
       if (error(...)) {
         goto finalize;
       }
       ...do more things...
    }
  }

  finalize:
    if (f) fclose(f);
    if (d) closeDatabaseConnection(d);
}        
于 2013-02-27T12:34:25.603 に答える