79

このスレッドではgoto、C または C++ での の適切な使用例を見ていきます。これは、私が冗談だと​​思ったために人々が投票した回答に触発されたものです。

要約(意図をより明確にするためにラベルをオリジナルから変更):

infinite_loop:

    // code goes here

goto infinite_loop;

代替案より優れている理由:

  • それは具体的です。 goto無条件分岐を引き起こす言語構造です。代替手段は、縮退した常に真の条件で、条件分岐をサポートする構造を使用することに依存します。
  • ラベルは、追加のコメントなしで意図を文書化します。
  • 読者は、介在するコードを初期の s でスキャンする必要はありませんbreak(ただし、非原理的なハッカーが初期の s でシミュレートする可能性はまだあります continue) goto

ルール:

  • ゴトフォブが勝てなかったふりをする。上記は確立されたイディオムに反するため、実際のコードでは使用できないことが理解されています。
  • 「Goto は有害と見なされる」という言葉を聞いたことがあると仮定し、goto を使用してスパゲッティ コードを記述できることを知っているとします。
  • 例に同意できない場合は、技術的なメリットだけで批判してください (「人々が goto を好まないから」は技術的な理由ではありません)。

これについて大人のように話せるか見てみましょう。

編集

この質問は現在終了しているようです。質の高い回答が得られました。みんな、特に私の小さなループの例を真剣に受け止めてくれた人たちに感謝します。ほとんどの懐疑論者は、ブロック スコープの欠如を懸念していました。@quinmars がコメントで指摘したように、いつでもループ本体を中かっこで囲むことができます。私はそれに注意し、中かっこも無料で提供for(;;)while(true)ません (また、中かっこを省略すると厄介なバグが発生する可能性があります)。とにかく、私はこのささいなことであなたの脳力をこれ以上浪費することはありません.私は無害で慣用的なものと一緒に暮らすことができfor(;;)ますwhile(true).

他の回答を考慮すると、多くの人gotoが常に別の方法で書き直さなければならないものと見なしているようです。もちろんgoto、ループ、余分なフラグ、ネストされた s のスタックなどを導入することで を回避できますが、がおそらくその仕事に最適なツールであるifかどうかを検討してみませんか? goto別の言い方をすれば、組み込みの言語機能を意図された目的のために使用することを避けるために、人々はどれだけの醜さに耐える準備ができているのでしょうか? 私の見解では、フラグを追加するだけでも、支払うには高すぎるということです。私は自分の変数が、問題または解決策のドメイン内のものを表すのが好きです。「回避するためだけにgoto」はそれをカットしません。

クリーンアップ ブロックに分岐するための C パターンを示した最初の回答を受け入れます。IMO、これはgoto、投稿されたすべての回答の中で最も強力なケースです。確かに、嫌いな人がそれを回避するために経験しなければならないゆがみによってそれを測定する場合.

4

16 に答える 16

87

これは、人々が使用していると聞いたトリックの 1 つです。でも、野生で見たことはありません。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;
}
于 2008-10-29T04:08:39.463 に答える
85

C での GOTO の古典的な必要性は次のとおりです。

for ...
  for ...
    if(breakout_condition) 
      goto final;

final:

goto を使用せずにネストされたループから抜け出す簡単な方法はありません。

于 2008-10-29T04:31:43.787 に答える
32

これは、シグナルによって中断される可能性のある Unix システムコールの (Stevens APITUE からの) 私のばかげていない例です。

restart:
    if (system_call() == -1) {
        if (errno == EINTR) goto restart;

        // handle real errors
    }

別の方法は縮退ループです。このバージョンは、英語のように「システム コールがシグナルによって中断された場合は、再起動してください」と読みます。

于 2008-10-29T04:07:39.293 に答える
14

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

ウィキペディアのエントリから上記のコード。

于 2008-10-29T04:08:23.723 に答える
14

Knuth は「GOTO ステートメントを使用した構造化プログラミング」という論文を書きました。たとえば、ここから入手できます。そこには多くの例があります。

于 2008-10-29T07:17:10.097 に答える
12

私は一般的にgotoに反対するものは何もありませんが、あなたが言及したようなループにgotoを使用したくないいくつかの理由を考えることができます:

  • スコープを制限しないため、内部で使用する一時変数は後で解放されます。
  • スコープを制限しないため、バグにつながる可能性があります。
  • スコープを制限しないため、後で同じスコープ内のコードで同じ変数名を再利用することはできません。
  • スコープを制限しないため、変数宣言をスキップする可能性があります。
  • 人々はそれに慣れておらず、コードが読みにくくなります。
  • このタイプのネストされたループはスパゲッティ コードにつながる可能性がありますが、法線ループはスパゲッティ コードにつながりません。
于 2008-10-29T04:01:57.527 に答える
12

ごく普通。

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)これにより、構造化された読みやすいコードを維持しながら、入れ子を増やす他の構成要素の悪用を回避できます。

于 2008-10-29T04:18:02.097 に答える
7

goto を使用するのに適した場所の 1 つは、さまざまなレベルのクリーンアップが必要ないくつかのポイントで中止できるプロシージャです。Gotophobes は常に goto を構造化されたコードと一連のテストに置き換えることができますが、過剰なインデントがなくなるため、これはより簡単だと思います。

if (!openDataFile())
  やめる;

if (!getDataFromFile())
  closeFileAndQuit に移動します。

if (!allocateSomeResources)
  freeResourcesAndQuit に移動します。

// ここでさらに作業を行います....

freeResourcesAndQuit:
   // リソースを解放する
closeFileAndQuit:
   // ファイルを閉じる
終了する:
   // 終了する!
于 2008-10-29T04:09:17.607 に答える
7

@fizzer.myopenid.com: 投稿されたコード スニペットは次のものと同等です。

    while (system_call() == -1)
    {
        if (errno != EINTR)
        {
            // handle real errors

            break;
        }
    }

私は間違いなくこのフォームを好みます。

于 2008-10-29T04:15:40.683 に答える
6

私は時間が経つにつれてこのパターンが嫌いになりましたが、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;
}
于 2008-10-29T05:07:00.720 に答える
5

以下は、適切な goto の例です。

// No Code
于 2008-10-29T04:01:13.160 に答える
1

gotoが正しく使用されているのを見てきましたが、状況は通常醜いです。それは、それ自体の使用がgotoオリジナルよりもはるかに悪い場合にのみです。@Johnathon Hollandの問題は、バージョンがあまり明確ではないということです。人々はローカル変数を恐れているようです:

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
}
于 2008-10-29T11:09:54.527 に答える
0
#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);
}
于 2014-01-09T21:58:52.753 に答える
0

これについての私の不満は、ブロックのスコープが失われることです。goto の間で宣言されたローカル変数は、ループが中断された場合でも有効なままです。(ループが永遠に続くと仮定しているかもしれませんが、元の質問者が求めていたのはそれではないと思います。)

一部のオブジェクトは、適切なタイミングで呼び出される dtor に依存している可能性があるため、スコープの問題は C++ の問題です。

私にとって、goto を使用する一番の理由は、失敗した場合にすべての init をバックアウトすることが不可欠な複数ステップの初期化プロセス中です。

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;
于 2008-10-29T04:11:44.793 に答える
0

私自身は goto を使用しませんが、特定の場合に goto を使用する人と仕事をしたことがあります。の記憶が正しければ、彼の理論的根拠はパフォーマンスの問題に関するものでした。常に同じ関数内にあり、ラベルは常に goto ステートメントの下にありました。

于 2008-10-29T05:53:48.753 に答える
-1

@グレッグ:

あなたの例を次のようにしてみませんか:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
于 2008-10-29T04:15:06.610 に答える