18

Cプログラムで次のコードを見つけました。

while (1)
{
    do_something();
    if (was_an_error()) break;

     do_something_else();
     if (was_an_error()) break;

     [...]

     break;
}
[cleanup code]

ここでwhile(1)は、「finally」のローカルエミュレーションとして使用されます。sを使用してこれを書くこともできますgoto

do_something()
if (was_an_error()) goto out;

do_something_else()
if (was_an_error()) goto out;

[...]
out:
[cleanup code]

gotoソリューションは通常のイディオムだと思いました。私はカーネルソースでこのイディオムのいくつかの出現を見てきました、そしてそれはDiomidisSpinellisの「CodeReading」本でも言及されています。

私の質問は:どの解決策がより良いですか?while(1)ソリューションを使用する特別な理由はありますか?

質問943826は私の質問に答えません。

4

13 に答える 13

34

一見普遍的なGOTOへの反発は、主にEdsgerDijkstraの手紙「有害と考えられる声明に進む」によるものです。

gotoを使用しないことにした場合は、次のようになります。

do {
    ...
while(0);

おそらく、while(1){...}よりも安全です。これは、誤ってループしないことが保証されるためです(また、誤ってループしている場合は、while(1)を使用すると、おそらく誤って無限にループします)。

この目的でdo/break/whileまたはwhile/breakをgotoよりも(ab)使用することの利点の1つは、構成のにジャンプしないことが保証されていることです。gotoを使用して、同じ内の以前のラベルにジャンプできます。働き。

do / break / whileなどがgotoに勝る欠点は、1つの出口ポイント(ループの直後)に制限されることです。場合によっては、段階的なクリーンアップが必要になることがあります。たとえば、ファイルハンドルを開くときに、メモリをmallocし、ファイルから読み取ります...読み取りが失敗した場合は、mallocをクリーンアップする必要があります。mallocが失敗した場合、それをクリーンアップする必要はありませんが、ファイルハンドルをクリーンアップする必要があります。gotoを使用すると、クリーンアップの段階ごとに1つのラベルを作成し、エラーが発生した場所に応じて正確に正しいポイントにジャンプできます。

私の意見では、GOTOに対する一般的な憎悪のために盲目的に回避することは、ケースバイケースで使用するためにケースを慎重に推論するよりも損害を与えます。私が使用する経験則は、「Linuxカーネルはそれを実行しますか?もしそうなら、それはそれほど悪いことではありません」ですLinuxカーネルを、最新のソフトウェアエンジニアリングの他の良い例に置き換えてください。

于 2009-07-02T10:18:45.970 に答える
10

コードを別の関数に入れ、それをreturn早期に終了するために使用することは、失敗の性質を示す戻りコードを簡単に統合できるという利点がある、それを行う別の方法です。

于 2009-07-02T09:44:57.787 に答える
8

私のスタイルが可能な限りクールではないことは知っていますが、特別な構成を必要とせず、簡潔で理解しにくいものではないため、私はそれを好みます。

エラー=(!エラー)&& do_something1();
エラー=(!エラー)&& do_something2();
エラー=(!エラー)&& do_something3();

//クリーンアップコード
于 2009-07-02T09:48:09.060 に答える
7

通常、gotoの使用は推奨されていませんが、あなたのようなまれな状況は、ベストプラクティスが最善ではない場所です。

したがって、gotoが最も明確なコードを作成する場合は、それを使用します。while(true)ループを使用してgotoをエミュレートするのは、不自然なことです。本当に必要なのは後藤です!

于 2009-07-02T09:35:00.457 に答える
5

一連のifステートメントを使用してみませんか?ループよりもはるかに明確であることがわかったので、私は通常このように記述します。

bool ok = true;

do_something();
if (was_an_error()) ok = false;

if (ok)
{
    do_something_else();
    if (was_an_error()) ok = false;
}

if (ok)
{
    do_something_else_again();
    if (was_an_error()) ok = false;
}

[...]

[Cleanup code]

また、厳密なコーディング標準に取り組んでいる場合、yesgotoは禁止される可能性がありますが、多くの場合禁止されているbreakためcontinue、ループは必ずしもそのための回避策ではありません。

于 2009-07-02T10:41:57.437 に答える
5

「break」はブロックスコープのセマンティクスを理解しますが、「goto」はそれを認識しません。つまり、「while-break」は末尾再帰を使用したLispのような関数型言語に変換できますが、「goto」は変換できません。

于 2009-07-08T23:28:47.400 に答える
3

通常、GOTOは悪いと見なされますが、GOTOを介したフォワードジャンプしかない場所では、それほど悪くはありません。人々はペストのようにGOTOを避けますが、GOTOのよく考えられた使用は、私見のより良い解決策である場合があります。

于 2009-07-02T09:38:09.750 に答える
2

このgotoの(リソース管理のための)使用は問題ないと思います。

于 2009-07-02T09:41:14.970 に答える
2

なんらかの理由でgotoを使用できない場合に使用します

  • プロジェクトの規則で禁止されています
  • リントツールで禁止されています

また、それはマクロが悪ではない場合の1つでもあると思います。

#define DO_ONCE for (int _once_dummy = 0; _once_dummy < 1; _once_dummy++)
于 2009-07-02T09:56:10.010 に答える
1

while(1)アプローチが好きです。自分で使っています。特に、要素がそのようなループ内で処理され、複数のアプローチで実行される場合など、ループが続行によって繰り返される可能性がある場合。

于 2009-07-02T09:41:51.303 に答える
1

永続的に真の条件で条件ループを使用しないでください。条件は常に真なので、なぜ条件付きループを使用するのですか?

永続的に真の条件は、gotoによって最も直接的に表されます。

于 2009-07-02T09:41:55.297 に答える
1

「dowhile」と「gotoout」は、これらの領域で異なります。

1.ローカル変数の初期化

void foo(bool t = false)
{
    if (t)
    {
        goto DONE;
    }

    int a = 10; // error : Goto bypass local variable's initialization 

    cout << "a=" << a << "\n";
DONE:
}

do ... while(0)ブロックでインプレースローカル変数を初期化するのは問題ありません。

void bar(bool t = false)
{
    do{
        if (t)
        {
            break; 
        }

        int a = 10;  // fine

        cout << "a=" << a << "\n";
    } while (0);

}

マクロの2つの違い。「dowhile」は少し良いです。マクロの「gotoDONE」はそうではありません。終了コードがより複雑な場合は、次のようになります。

err = some_func(...);
if (err)
{
    register_err(err, __LINE__, __FUNC__);
#if defined (_DEBUG)
    do_some_debug(err)
#endif
    break;
}

そして、このコードを何度も書くと、おそらくそれらをマクロに入れるでしょう。

#define QUIT_IF(err)                     \
if (err)                                       \
{                                              \
    register_err(err, __LINE__, __FUNC__);     \
    DO_SOME_DEBUG(err)                         \
    break; // awful to put break in macro, but even worse to put "goto DONE" in macro.  \
}

そして、コードは次のようになります。

do
{
    initial();

    do 
    {
        err = do_step1();
        QUIT_IF(err);

        err = do_step2();
        QUIT_IF(err);

        err = do_step3();
        QUIT_IF(err);

        ....
    } while (0);
    if (err) {     // harder for "goto DONE" to get here while still using macro.
        err = do_something_else();
    }
    QUIT_IF(err);
    .....
} while (0);

3.do ... while(0)は、同じマクロでさまざまなレベルの終了を処理します。コードは上に示されています。goto ...はマクロには当てはまりません。これは、レベルごとに異なるラベルが必要になるためです。

そういえば、どちらも好きではありません。例外メソッドを使用したいと思います。例外が許可されていない場合は、「do ... while(0)」を使用します。ブロック全体がインデントされているため、「gotoDONE」スタイルよりも実際に読みやすくなっています。

于 2013-08-29T17:37:56.347 に答える
0

エラー処理の状況で「goto」を使用することはかなり一般的ですが、それでも「while」ソリューション(または「dowhile」)を使用したいと思います。「goto」の場合、コンパイラーが保証できるものははるかに少なくなります。ラベル名にタイプミスをすると、コンパイラはそこであなたを助けることができません。誰かがそのブロック内の別のラベルに別のgotoを使用した場合、クリーンアップコードが呼び出されない可能性が高くなります。より構造化されたフロー制御構造を使用すると、ループが終了するとどのコードが実行されるかが常に保証されます。

于 2009-07-02T09:45:08.037 に答える