これはCのgotoの適切なユースケースですか?


59

私は「議論、議論、投票、または議論を求める」ことを望んでいないので、これを尋ねることを本当にbutしますが、私はCに不慣れであり、言語で使用される一般的なパターンについてより多くの洞察を得たいと思っています。

私は最近、gotoコマンドに対する嫌悪感を聞いたが、最近、それに対するまともなユースケースも見つけた。

このようなコード:

error = function_that_could_fail_1();
if (!error) {
    error = function_that_could_fail_2();
    if (!error) {
        error = function_that_could_fail_3();
        ...to the n-th tab level!
    } else {
        // deal with error, clean up, and return error code
    }
} else {
    // deal with error, clean up, and return error code
}

クリーンアップ部分がすべて非常に類似している場合、次のように少しきれいに書くことができます(私の意見ですか?)。

error = function_that_could_fail_1();
if(error) {
    goto cleanup;
}
error = function_that_could_fail_2();
if(error) {
    goto cleanup;
}
error = function_that_could_fail_3();
if(error) {
    goto cleanup;
}
...
cleanup:
// deal with error if it exists, clean up
// return error code

これgotoはCの一般的または許容可能なユースケースですか?これを行うための別の/より良い方法はありますか?


3
この質問(および私の回答)も参照してください。
キーストンプソン

2
コンパイラはシュガーコーティングよりもクリーンアップとエラーチェックを保証しますが、Cでは手動で行う必要があり、DRYに違反し、ある場所ではエラーコードをチェックするのが面倒であるか、間違ったラベルなどに行ったことを保証します。
DeadMG

3
@Izkata:いいえ、ポイントは、例外がスローされたときにコンパイラーによって暗黙的に呼び出される関数(デストラクターなど)を持つことです。
DeadMG

6
@DeadMG:別の言語を使用することが常にオプションとは限りません。
ベンジャミンクロスター

13
適切なラベルの名前付けをすると、例はかなりまともなように見えるかもしれませんgoto hell;
。– gnat

回答:


48

gotoステートメント(およびそれに対応するラベルが)フロー制御であるプリミティブ(文の条件付きの実行に伴って)。つまり、プログラムフロー制御ネットワークを構築できるようにするためです。それらは、フローチャートのノード間の矢印をモデル化すると考えることができます。

これらの一部は、直接線形フローがある場合にすぐに最適化できます(基本的なステートメントのシーケンスを使用するだけです)。他のパターンは、これらが利用可能な場合、構造化プログラミング構造に置き換えるのが最適です。whileループのように見える場合は、ループを使用してwhileください、OK?構造化されたプログラミングパターンは、文の混乱よりも少なくとも潜在的に明確に意図を明確にしていgotoます。

ただし、Cには、考えられるすべての構造化プログラミング構造が含まれているわけではありません。(関連するすべてのものがまだ発見されていることは私には明らかではありません;発見の速度は今遅いですが、私はすべてが発見されたと言うことにjumpすることをheします。)try/ catch/ finally構造(および例外も)。また、マルチレベルのbreak-from-loop もありません。これらは、goto実装に使用できる種類のものです。それはあまりにもこれらを行うために他のスキームを使用することが可能です-私たちは、Cが持っていることを知っています十分な非のセットをgotoプリミティブ—しかし、これらにはフラグ変数の作成と、より複雑なループまたはガード条件の作成が含まれます。制御分析とデータ分析の絡み合いを増やすと、プログラム全体の理解が難しくなります。また、コンパイラーによる最適化とCPUの迅速な実行がより困難になります(ほとんどのフロー制御構造は、そして間違いなく goto非常に安価です)。

したがって、goto必要でない限り使用するべきではありませんが、それが存在し、必要になる可能性があることを認識しておく必要があります。必要な場合は、気分を悪くするべきではありません。必要な場合の例は、呼び出された関数がエラー状態を返すときのリソースの割り当て解除です。(つまり、try/ finally。)それを書かなくてもgoto、それを行うことは、それを維持する問題のように、それ自身の欠点がある可能性があります。ケースの例:

int frobnicateTheThings() {
    char *workingBuffer = malloc(...);
    int i;

    for (i=0 ; i<numberOfThings ; i++) {
        if (giveMeThing(i, workingBuffer) != OK)
            goto error;
        if (processThing(workingBuffer) != OK)
            goto error;
        if (dispatchThing(i, workingBuffer) != OK)
            goto error;
    }

    free(workingBuffer);
    return OK;

  error:
    free(workingBuffer);
    return OOPS;
}

コードはさらに短くなる可能性がありますが、ポイントを実証するには十分です。


4
1:C後藤は「必要」は技術的になることはありません-それを行うための方法が常にある、それはMISRA Cでのgotoの外観を使用するためのガイドラインの堅牢なセットのために.....散らかっ取得
mattnz

1
あなたが希望try/catch/finallygoto使用可能ですスパゲッティコードの(それが複数の機能/モジュール全体に広がることが可能と)似て、より一層普及形にもかかわらずtry/catch/finally
自閉症

65

はい。

たとえば、Linuxカーネルで使用されます。これは、10年近く前のスレッドの終わりからのメールです。

From:Robert Love
Subject:Re:2.6.0-test *の可能性はありますか?
日付:2003年1月12日17:58:06 -0500 2003年1月12日

17:22の日曜日、Rob Wilkensは次のように書いています:

「gotoを使用しないでください」と言い、代わりに「cleanup_lock」関数を使用して、すべてのreturnステートメントの前に追加します。これは負担になりません。はい、それは開発者に少し一生懸命働くことを求めていますが、最終的な結果はより良いコードです。

いいえ、それはひどく、カーネルを肥大化します。終了コードを最後に1回持つのではなく、N個のエラーパスに対するジャンクの束をインライン化します。 キャッシュフットプリントは重要であり、あなたはそれを殺しました。

また、読みやすくなっています。

最後の引数として、それは私たちがきれいに通常のスタック風の風を行うとくつろぐことはできません。すなわち、

        do A
        if (error)
            goto out_a;
        do B
        if (error)
            goto out_b;
        do C
        if (error)
            goto out_c;
        goto out;
        out_c:
        undo C
        out_b:
        undo B:
        out_a:
        undo A
        out:
        return ret;

これをやめてください。

ロバート・ラブ

それはそれはあなたがスピードと(カーネルや組込みシステムなど)、低メモリフットプリントを必要とする何かを書いている場合を除きので、あなたがしなければならない、あなたはgoto文を使用して慣れるスパゲッティコードの作成から自分を保つために規律の多くを必要と述べた本当に最初のgotoを書く前にそれについて考えてください。


21
カーネルは、生の速度と読みやすさの優先順位に関して、カーネル以外のプログラムとは異なることに注意してください。言い換えれば、彼らはすでにプロファイルを作成しており、gotoで速度を上げるためにコードを最適化する必要があることがわかりました。

11
スタックのアンワインドを使用して、実際にスタックにプッシュすることなくエラー時のクリーンアップを処理します!これはgotoの素晴らしい使い方です。
mike30

1
@ user1249、Rubbish、{library、kernel}コードを使用するすべての{past、existing、future}アプリのプロファイルを作成することはできません。あなたは単に速くする必要があります。
17

1
無関係:人々がメーリングリストを使用して、このような大規模なプロジェクトはもちろんのこと、何かを成し遂げることができることに驚いています。それはまさに...原始的です。人々はどのようにメッセージの消防署に来ますか?!
アレクサンダー

2
節度についてはあまり心配していません。誰かがインターネット上のある嫌いな人に背を向けられるほど柔らかいなら、あなたのプロジェクトはおそらく彼らなしで良いでしょう。私は、受信メッセージの集中砲火に遅れずについていくのが非現実的であり、例えば、引用符を追跡するためのツールがほとんどない自然な前後の議論をどのように行うことができるかについて、より懸念しています。
アレクサンダー

14

私の意見では、あなたが投稿したコードはの有効な使用例ですgoto。なぜなら、下にジャンプするだけで、プリミティブな例外ハンドラのようにしか使用しないからです。

ただし、古いgotoの議論のため、プログラマーはgoto約40年間回避しているため、gotoを使用してコードを読み取るために使用されていません。これはgotoを避ける正当な理由です。単に標準ではありません。

私は、Cプログラマーがより読みやすいコードとしてコードを書き直したでしょう。

Error some_func (void)
{
  Error error;
  type_t* resource = malloc(...);

  error = some_other_func (resource);

  free (resource);

  /* error handling can be placed here, or it can be returned to the caller */

  return error;
}


Error some_other_func (type_t* resource)  // inline if needed
{
  error = function_that_could_fail_1();
  if(error)
  {
    return error;
  }

  /* ... */

  error = function_that_could_fail_2();
  if(error)
  {
    return error;
  }

  /* ... */

  return ok;
}

この設計の利点:

  • 実際の作業を行う関数は、データの割り当てなど、アルゴリズムに関係のないタスクに関与する必要はありません。
  • Cプログラマーはgotoやlabelを恐れているので、コードはCプログラマーには馴染みがありません。
  • アルゴリズムを実行する関数の外部で、エラー処理と割り当て解除を同じ場所で一元化できます。関数が独自の結果を処理することは意味がありません。


9

Javaでは、次のようにします。

makeCalls:  {
    error = function_that_could_fail_1();
    if (error) {
        break makeCalls;
    }
    error = function_that_could_fail_2();
    if (error) {
        break makeCalls;
    }
    error = function_that_could_fail_3();
    if (error) {
        break makeCalls;
    }
    ...
    return 0;  // No error code.
}
// deal with error if it exists, clean up
// return error code

これをよく使います。が嫌いなgotoので、他のほとんどのCスタイル言語ではコードを使用しています。他に良い方法はありません。(ネストされたループからのジャンプも同様のケースです。Javaではラベル付きbreakを使用し、他のすべてではgoto。を使用します。)


3
それはきちんとした制御構造です。
ブライアンベッチャー

4
これは本当に面白いです。私は通常、Javaでこれにtry / catch / finally構造を使用することを考えます(破損する代わりに例外をスローします)。
ロブズ

5
それは本当に読めません(少なくとも私にとって)。存在する場合、例外ははるかに優れています。
m3th0dman

1
@ m3th0dmanこの特定の例(エラー処理)に同意します。しかし、このイディオムが役に立つ他の(非例外的な)ケースがあります。
コンラッドルドルフ

1
例外は高価であり、エラー、スタックトレース、さらに多くのジャンクを生成する必要があります。このラベルブレークにより、チェックループから完全に抜けます。メモリと速度を気にしない限り、私はすべて例外を使用します。
-Tschallacka

8

私はそれまともなユースケースだと思いますが、「エラー」がブール値にすぎない場合、あなたが望むことを達成するための別の方法があります:

error = function_that_could_fail_1();
error = error || function_that_could_fail_2();
error = error || function_that_could_fail_3();
if(error)
{
     // do cleanup
}

これはブール演算子の短絡評価を利用します。これが「より良い」なら、あなたの個人的な好みとそのイディオムにどのように慣れているかによります。


1
これに伴う問題は、errorすべてのORで値が無意味になる可能性があることです。
ジェームズ

@James:原因あなたのコメントに私の答えを編集した
ドク・ブラウン

1
これでは不十分です。最初の機能でエラーが発生した場合、2番目または3番目の機能を実行したくありません。
ロブズ

2
した場合、短手の評価あなたは意味短絡評価を、これは、ビット単位のORではなく論理和を使用することにここで行われているものを正確ではありません。
クリスは、モニカを復活させる

1
@ChristianRau:おかげで、それに応じて私の答えを編集した
ドク・ブラウン

6

Linuxスタイルガイドにはgoto、例に沿ったを使用する特定の理由が記載されています。

https://www.kernel.org/doc/Documentation/process/coding-style.rst

gotosを使用する理由は次のとおりです。

  • 無条件のステートメントは理解しやすく、従うのが簡単です
  • ネスティングが削減されます
  • 変更を行うときに個々の出口点を更新しないことによるエラー
  • 冗長コードを最適化するためのコンパイラの作業を節約します;)

免責事項私は自分の作品を共有することになっていない。ここの例は少し工夫されているので、ご容赦ください。

これはメモリ管理に適しています。私は最近、メモリを動的に割り当てたコード(たとえばchar *、関数から返されたコード)に取り組みました。パスを見て、パスのトークンを解析することでパスが有効かどうかを確認する関数:

tmp_string = strdup(string);
token = strtok(tmp_string,delim);
while( token != NULL ){
    ...
    some statements, some involving dynamically allocated memory
    ...
    if ( check_this() ){
        free(var1);
        free(var2);
        ...
        free(varN);
        return 1;
    }
    ...
    some more stuff
    ...
    if(something()){
        if ( check_that() ){
            free(var1);
            free(var2);
            ...
            free(varN);
            return 1;
        } else {
            free(var1);
            free(var2);
            ...
            free(varN);
            return 0;
        }
    }
    token = strtok(NULL,delim);
}

free(var1);
free(var2);
...
free(varN);
return 1;

さて、次のコードは、aを追加する必要がある場合に、はるかに優れており、保守が容易ですvarNplus1

int retval = 1;
tmp_string = strdup(string);
token = strtok(tmp_string,delim);
while( token != NULL ){
    ...
    some statements, some involving dynamically allocated memory
    ...
    if ( check_this() ){
        retval = 1;
        goto out_free;
    }
    ...
    some more stuff
    ...
    if(something()){
        if ( check_that() ){
            retval = 1;
            goto out_free;
        } else {
            retval = 0;
            goto out_free;
        }
    }
    token = strtok(NULL,delim);
}

out_free:
free(var1);
free(var2);
...
free(varN);
return retval;

現在、コードにはあらゆる種類の問題があります。つまり、Nは10を超えており、関数は450行を超えており、場所によっては10レベルの入れ子になっています。

しかし、私はスーパーバイザーにそれをリファクタリングするように提供しました。それは私がやったもので、今ではすべてが短い機能の集まりであり、すべてLinuxスタイルを持っています

int function(const char * param)
{
    int retval = 1;
    char * var1 = fcn_that_returns_dynamically_allocated_string(param);
    if( var1 == NULL ){
        retval = 0;
        goto out;
    }

    if( isValid(var1) ){
         retval = some_function(var1);
         goto out_free;
    }

    if( isGood(var1) ){
         retval = 0;
         goto out_free;
    }

out_free:
    free(var1);
out:
    return retval;
}

gotos なしの同等物を検討する場合:

int function(const char * param)
{
    int retval = 1;
    char * var1 = fcn_that_returns_dynamically_allocated_string(param);
    if( var1 != NULL ){

       if( isValid(var1) ){
            retval = some_function(var1);
       } else {
          if( isGood(var1) ){
               retval = 0;
          }
       }
       free(var1);

    } else {
       retval = 0;
    }

    return retval;
}

私にとって、最初のケースでは、最初の関数が戻るとNULL、ここから離れて戻りますことは明らかです0。2番目のケースでは、ifに関数全体が含まれていることを確認するために、下にスクロールする必要があります。最初の1つはスタイルを示して( " out" という名前)、2つ目はそれを構文的に示しています。最初のものはさらに明白です。

また、free()関数の最後にステートメントを置くことを非常に好みます。それは、私の経験でfree()は、関数の途中のステートメントが悪臭を放ち、サブルーチンを作成する必要があることを示しているからです。この場合、var1関数で作成し、free()サブルーチンでは作成できませんでしたが、そのためgoto out_free、goto outスタイルが非常に実用的です。

プログラマーは、goto「は悪だ」と信じて育てられる必要があると思います。その後、十分に成熟したら、Linuxソースコードを参照し、Linuxスタイルガイドを読む必要があります。

すべての関数にはint retvalout_freelabel、およびoutラベルがあり、このスタイルを非常に一貫して使用することを追加する必要があります。文体の一貫性により、読みやすさが向上しています。

ボーナス:中断して続行

whileループがあるとします

char *var1, *var2;
char line[MAX_LINE_LENGTH];
while( sscanf(line,... ){
    var1 = functionA(line,count);
    var2 = functionB(line,count);

    if( functionC(var1, var2){
         count++
         continue;
    }

    ...
    a bunch of statements
    ...

    count++;
    free(var1);
    free(var2);
}

このコードには他にも問題がありますが、1つはcontinueステートメントです。全体を書き直したいと思いますが、小さな方法で修正する必要がありました。それを満足させる方法でリファクタリングするには数日かかりましたが、実際の変更は約半日の作業でした。問題は、私たちは'場合でも、ということであるcontinue私たちがまだ解放する必要があるvar1var2。を追加するvar3必要があり、free()ステートメントをミラーリングする必要があることを嫌になりました。

当時私は比較的新しいインターンでしたが、しばらく前からLinuxのソースコードを見ていたので、gotoステートメントを使用できるかどうかをスーパーバイザーに尋ねました。彼はイエスと言った、そして私はこれをした:

char *var1, *var2;
char line[MAX_LINE_LENGTH];
while( sscanf(line,... ){
    var1 = functionA(line,count);
    var2 = functionB(line,count);
    var3 = newFunction(line,count);

    if( functionC(var1, var2){
         goto next;
    }

    ...
    a bunch of statements
    ...
next:
    count++;
    free(var1);
    free(var2);
}

継続することは良くても大丈夫だと思いますが、私にとってはそれらは目に見えないラベルのついた後藤のようなものです。同じことが休憩にも当てはまります。ここでの場合のように、複数の場所で変更をミラーリングすることを強制しない限り、継続または中断を選択します。

また、このラベルgoto next;next:ラベルの使用は私にとって不満足であると付け加えるべきです。それらは単にfree()「」と「count++ステートメント」をミラーリングするよりも優れています。

gotoはほとんどの場合間違っていますが、いつ使用するのが適切かを知る必要があります。

私が議論しなかったことの1つは、他の回答でカバーされているエラー処理です。

性能

strtok()http://opensource.apple.com//source/Libc/Libc-167/string.subproj/strtok.cの実装を見ることができます

#include <stddef.h>
#include <string.h>

char *
strtok(s, delim)
    register char *s;
    register const char *delim;
{
    register char *spanp;
    register int c, sc;
    char *tok;
    static char *last;


    if (s == NULL && (s = last) == NULL)
        return (NULL);

    /*
     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
     */
cont:
    c = *s++;
    for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
        if (c == sc)
            goto cont;
    }

    if (c == 0) {       /* no non-delimiter characters */
        last = NULL;
        return (NULL);
    }
    tok = s - 1;

    /*
     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
     * Note that delim must have one NUL; we stop if we see that, too.
     */
    for (;;) {
        c = *s++;
        spanp = (char *)delim;
        do {
            if ((sc = *spanp++) == c) {
                if (c == 0)
                    s = NULL;
                else
                    s[-1] = 0;
                last = s;
                return (tok);
            }
        } while (sc != 0);
    }
    /* NOTREACHED */
}

間違っている場合は修正してください。ただし、cont:ラベルとgoto cont;ステートメントはパフォーマンスのためにあると考えています(コードをより読みやすくしないでください)。これらを実行することにより、読み取り可能なコードに置き換えることができます

while( isDelim(*s++,delim));

区切り文字をスキップします。しかし、可能な限り高速になり、不必要な関数呼び出しを避けるために、彼らはこのようにします。

ダイクストラの論文を読みましたが、かなり難解です。

google「dijkstra gotoステートメントは有害と見なされます」というのは、3つ以上のリンクを投稿するほどの評判がないからです。

gotoを使用しない理由として引用されているのを見てきましたが、gotoの使用に関しては、これを読んでも何も変わりません。

補遺

継続と中断についてこれについてすべて考えながら、きちんとしたルールを思いつきました。

  • whileループ内にcontinueがある場合、whileループの本体は関数であり、continueはreturnステートメントでなければなりません。
  • whileループ内にbreakステートメントがある場合、whileループ自体が関数であり、breakがreturnステートメントになる必要があります。
  • 両方がある場合、何かが間違っている可能性があります。

スコープの問題のために常に可能とは限りませんが、これを行うとコードについて推論するのがはるかに簡単になることがわかりました。whileループが中断または継続するたびに不快感を覚えることに気付きました。


2
+1ですが、1つの点で意見が一致しませんか?「プログラマーは、gotoが悪であると信じて育てられる必要があると思います。」そうかもしれませんが、1975年にテキストエディタなしで行番号とGOTOを使用してBASICでプログラミングすることを最初に学びました。10年後、構造化プログラミングに出会いました。停止する圧力。今日、私は様々な理由で年に数回GOTOを使用していますが、あまり出ていません。GOTOが悪であると信じるように育てられなかったからといって、私が知っている害はありませんでした。それは私だけです。
-thb

1
あなたはそれについて正しいと思います。GOTOを使用しないという考えに思いつきました。純粋に偶然、メモリを解放する複数の出口点を備えたこれらの機能を備えたコードに取り組んでいたときに、Linuxソースコードを参照していました。そうでなければ、私はこれらのテクニックについて知らなかったでしょう。
フィリップカーフィン

1
@thbまた、面白い話、私は当時のスーパーバイザーにインターンとしてGOTOを使用する許可を求めました。そして、私は彼に、GOTOで使用されるような特定の方法でそれらを使用することを説明したことを確認しましたLinuxカーネルと彼は「わかりました、それは理にかなっています。また、CでGOTOを使用できるとは知りませんでした」。
フィリップカーフィン

1
@thb このように(ループを壊すのではなく)ループに入るのが良いかどうかわかりませんか?まあ、それは悪い例ですが、gotoステートメントを使用したKnuthの構造化プログラミングの gotoステートメントを使用したクイックソート(例7a)はあまり理解できないことがわかりました。
Yai0Phah

@ Yai0Phah私は私のポイントを説明しますが、私の説明はあなたの素晴らしい例7aを減少させません!私は例を承認します。それでも、偉い2年生は後藤について人々に講義するのが好きです。1985年以降、重大な問題を引き起こすgotoの実用的な使用法を見つけることは困難ですが、プログラマーの作業を容易にする無害なgotoを見つけることができます。とにかく、後藤は現代のプログラミングではめったに発生しないので、発生した場合、私のアドバイスは、それを使用したい場合は、おそらくそれを使用する必要があります。後藤は元気です。gotoの主な問題は、gotoを非推奨にすることでスマートに見えると考える人がいることです。
THB

5

個人的には、次のようにリファクタリングします。

int DoLotsOfStuffThatCouldFail (paramstruct *params)
{
    int errcode = EC_NOERROR;

    if ((errcode = FunctionThatCouldFail1 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail2 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail3 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail4 (params)) != EC_NOERROR) return errcode;

    return EC_NOERROR;
}

void DoStuff (paramstruct *params)
{
    int errcode = EC_NOERROR;

    InitStuffThatMayNeedToBeCleaned (params);

    if ((errcode = DoLotsOfStuffThatCouldFail (params)) != EC_NOERROR)
    {
         CleanupAfterError (params, errcode);
    }
}

ただし、gotoを回避するよりも深いネストを回避する方が動機付けになり(IMOは最初のコードサンプルでのより悪い問題)、もちろんCleanupAfterErrorがスコープ外であることに依存します(この場合、「params」は解放する必要がある割り当てられたメモリ、閉じる必要があるFILE *などを含む構造体です)。

このアプローチの主な利点の1つは、たとえばFTCF2とFTCF3の間に仮想の将来の追加ステップを挿入する(または既存の現在のステップを削除する)ことが簡単でクリーンであるため、保守性(および私をリンチしたくないコードを継承します!)-さておき、ネストされたバージョンにはそれがありません。


1
私は質問でこれを述べませんでしたが、FTCFが同じパラメーターを持たない可能性があり、このパターンをもう少し複雑にします。どうもありがとう。
ロブズ

3

厳格な基準の下でgotoを許可するMISRA(自動車産業ソフトウェア信頼性協会)Cコーディングガイドラインをご覧ください(例を満たしている場合)

私が働いている場所で同じコードが書かれているでしょう-後藤は必要ありません-それらについての不必要な宗教的議論を避けることは、どんなソフトウェアハウスでも大きなプラスです。

error = function_that_could_fail_1();
if(!error) {
  error = function_that_could_fail_2();
}
if(!error) {
  error = function_that_could_fail_3();
} 
if(!error) {
...
if (error) {
  cleanup:
} 

または「goto in drag」-gotoよりもさらに危険なものですが、「No goto Ever !!!」を回避します camp)「きっと大丈夫だろう、後藤は使わない」

do {
  if (error = function_that_could_fail_1() ){
    break 
  }
  if (error = function_that_could_fail_2() ){
    break 
  }
  ....... 
} while (0) 
cleanup();
.... 

関数のパラメータータイプが同じ場合は、テーブルに入れてループを使用します-


2
現在のMISRA-C:2004ガイドラインでは、どのような形式でもgotoを許可していません(ルール14.4を参照)。MISRA委員会は常にこれについて混乱していることに注意してください。彼らはどの足に立つべきかを知りません。最初に、彼らはgoto、continueなどの使用を無条件に禁止しました。しかし、次のMISRA 2011のドラフトでは、再び許可することを望んでいます。副次的に、MISRAは、gotoを使用するよりもはるかに危険であるため、非常に正当な理由でif文内の割り当てを禁止していることに注意してください。

1
分析の観点から、プログラムにフラグを追加することは、フラグがスコープ内にあるすべてのコードコードを複製し、if(flag)1つのコピーごとに「if」ブランチを取得し、他のコピーの対応する各ステートメントに「他」。フラグを設定およびクリアするアクションは、実際にはこれら2つのバージョンのコード間をジャンプする「ゴト」です。フラグの使用が他の方法よりもクリーンである場合がありますが、1つのgotoターゲットを保存するためにフラグを追加することは良いトレードオフではありません。
supercat

1

またgoto、代替do/while/continue/breakハッカーの可読性が低い場合にも使用します。

gotoターゲットには名前があり、読み取りますgoto something;。これは、より読みやすいかもしれbreakまたはcontinueあなたが実際に何かを停止するか、継続していない場合。


4
do ... while(0)実際のループではなく、の使用を防ぐための頭脳的な試みである、または別の構造内のどこでもgoto
aib

1
ああ、ありがとう、私はこの特定のブランド「なぜ誰かがそれをするのか!」まだ構築します。
ベンジャミンクロスター

2
通常、do / while / continue / breakハッカリーは、そもそもそれを含むモジュールが非常に長く複雑すぎる場合にのみ読み取り不可能になります。
ジョンR.ストローム

2
gotoを使用する理由として、これには何も見つかりません。中断して続行すると、明らかな結果が生じます。goto ... where?ラベルはどこにありますか?Breakとgotoは、次のステップがどこにあり、その近くにあることを正確に伝えます。
リグ

1
もちろん、ラベルはループ内から見えるはずです。私は@John R. Strohmのコメントの長さの一部に同意します。そして、ループハッカリーに翻訳されたあなたのポイントは、「何から抜け出せ?これはループではない!」になります。いずれにせよ、これはOPが恐れているものになりつつあるので、議論を放棄しています。
aib

-1
for (int y=0; y<height; ++y) {
    for (int x=0; x<width; ++x) {
        if (find(x, y)) goto found;
    }
}
found:

ループが1つだけの場合、スティグマはありませんが、のbreakようgotoに機能します。
9000

6
-1:最初に、xおよびyはfound:の範囲外です。したがって、これは役に立ちません。第二に、記述されたコードでは、あなたがたどり着いたという事実は、あなたが探していたものを見つけたという意味ではありません。
ジョンR.ストローム

これは、複数のループから抜け出す場合に考えられる最小の例だからです。より良いラベルや完成したチェックのために自由に編集してください。
aib

1
しかし、C関数は必ずしも副作用がないとは限らないことにも留意してください。
aib

1
@ JohnR.Strohmこれはあまり意味がありません...「found」ラベルは、変数をチェックするためではなく、ループを破るために使用されます。変数を確認したい場合、次のようなことができます:for(int y = 0; y <height; ++ y){for(int x = 0; x <width; ++ x){if(find( x、y)){doSomeThingWith(x、y); 後藤が見つかりました。}}}が見つかりました:
YoYoYonnY

-1

ある方法は受け入れられ、別の方法は受け入れられないと言うキャンプが常にあります。私が働いてきた会社は、gotoの使用を渋っていたり、強く落胆させてきました。個人的には、使用したことがあるとは思いませんが、だからといってそれらが悪いというわけではありません。

Cでは、通常次のことを行います。

  • 処理(不正な入力など)および「戻り」を妨げる可能性のある条件をテストします。
  • リソースの割り当てを必要とするすべての手順を実行します(例:mallocs)
  • 複数のステップで成功を確認する処理を実行します
  • 正常に割り当てられた場合、リソースを解放します
  • 結果を返す

gotoの例を使用して処理するには、次のようにします。

エラー= function_that_could_fail_1(); if(!error){error = function_that_could_fail_2(); } if(!error){error = function_that_could_fail_3(); }

ネストはなく、if句内では、ステップでエラーが生成された場合、エラー報告を行うことができます。したがって、gotoを使用する方法よりも「悪い」必要はありません。

誰かが別の方法で実行できないgotoを持ち、同じように読み取り可能/理解可能であるケースに出くわすことはまだありません。それが私見です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.