web-dev-qa-db-ja.com

CまたはC ++の良いgotoの例

このスレッドでは、CまたはC++でのgotoの適切な使用例を見ていきます。それは 回答 に触発されたものです。

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

_infinite_loop:

    // code goes here

goto infinite_loop;
_

他の選択肢よりも優れている理由:

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

ルール:

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

大人のようにこれについて話すことができるかどうか見てみましょう。

編集

この質問は終了したようです。質の高い回答が生成されました。みんな、特に私の小さなループの例を真剣に考えてくれた人たちに感謝します。ほとんどの懐疑論者は、ブロックスコープの欠如に懸念を抱いていました。 @quinmarsがコメントで指摘したように、ループ本体の周りにいつでも中括弧を置くことができます。 for(;;)while(true)はどちらも無料で中括弧を与えないことに注意してください(それらを省略すると厄介なバグが発生する可能性があります)。とにかく、私はこの些細なことであなたの脳力をこれ以上浪費しません-私は無害で慣用的なfor(;;)while(true)で生きることができますジョブ)。

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

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

79
fizzer

人々が使用していると聞いたことがあります。私は野生でそれを見たことがありません。また、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;
}
87
Greg Rogers

CでのGOTOの典型的なニーズは次のとおりです。

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

final:

Gotoなしでネストされたループを抜け出す簡単な方法はありません。

85
Paul Nathan

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

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

        // handle real errors
    }

代替手段は縮退ループです。このバージョンは、「システムコールがシグナルによって中断された場合、それを再起動する」英語のように読みます。

32
fizzer

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

wikipediaの上記のコード entry

14
Mitch Wheat

Knuthは「GOTOステートメントを使用した構造化プログラミング」という論文を書いています。 from here 。そこには多くの例があります。

14
zvrba

ごく普通。

_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)およびネストを増やすその他の構成要素の悪用を回避しながら、読み取り可能な構造化コードを維持します。

12
ephemient

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

  • スコープを制限しないため、内部で使用する一時変数は後ほど解放されません。
  • スコープを制限しないため、バグにつながる可能性があります。
  • スコープを制限しないため、将来同じコードで同じ変数名を将来のコードで再利用することはできません。
  • スコープを制限しないため、変数宣言をスキップする可能性があります。
  • 人々はそれに慣れていないため、コードが読みにくくなります。
  • このタイプのネストされたループはスパゲッティコードにつながる可能性がありますが、法線ループはスパゲッティコードにつながりません。
12
Brian R. Bondy

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

 if(!openDataFile())
 goto quit; 
 
 if(!getDataFromFile())
 goto closeFileAndQuit; 
 
 if(!allocateSomeResources)
 goto freeResourcesAndQuit; 
 
 //ここでさらに作業を行います。..
 
 freeResourcesAndQuit: 
 //空きリソース
 closeFileAndQuit:
 //ファイルを閉じる
 quit:
 //終了!
7
Adam Liss

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

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

            break;
        }
    }

私は間違いなくこの形式を好む。

7
Mitch Wheat

時間が経つにつれてこのパターンを嫌うようになりましたが、それは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;
}
6
JaredPar

良いgotoの例を次に示します。

// No Code
5
FlySwat

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
}
_
1
Charles Beattie

これに関する私の不満は、ブロックスコープを失うことです。 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;
0
Jim Nelson
#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);
}
0
StrifeSephiroth

私自身はgotoを使用していませんが、特定のケースでgotoを使用することがある人と一緒に仕事をしました。私の記憶が正しければ、彼の理論的根拠はパフォーマンスの問題に関するものでした-彼はまた、howのための特定のルールを持っていました。常に同じ機能であり、ラベルは常にgotoステートメントの下にあります。

0
user25306