歴史的背景: ダイクストラが 後藤は有害と見なされる と書いたことを覚えておく必要があります。 多くのプログラマーが goto
を使用していた 1968 年に 構造化プログラミングの代替として (if
、 while
、 for
など)。
それから 44 年が経ち、goto
がこのように使われることはめったにありません。 野生で。構造化プログラミングは、ずっと前にすでに勝利を収めています。
ケース分析:
サンプルコードは次のようになります:
SETUP...
again:
COMPUTE SOME VALUES...
if (cmpxchg64(ptr, old_val, val) != old_val)
goto again;
構造化されたバージョンは次のようになります:
SETUP...
do {
COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);
構造化されたバージョンを見ると、すぐに「ループだ」と思います。 goto
を見ると バージョンでは、最後に「再試行」の場合がある直線と考えています。
goto
バージョンには両方の SETUP
があります と COMPUTE SOME VALUES
これは、ほとんどの場合、制御フローが両方を通過することを強調しています。構造化バージョンは SETUP
を置きます および COMPUTE SOME VALUES
ここでの質問は、コードにどのような強調を加えたいかということです。これを goto
と比較できます エラー処理:
構造化バージョン:
if (do_something() != ERR) {
if (do_something2() != ERR) {
if (do_something3() != ERR) {
if (do_something4() != ERR) {
...
バージョンに移動:
if (do_something() == ERR) // Straight line
goto error; // |
if (do_something2() == ERR) // |
goto error; // |
if (do_something3() == ERR) // |
goto error; // V
if (do_something4() == ERR) // emphasizes normal control flow
goto error;
生成されるコードは基本的に同じなので、インデントなどのタイプミスと考えることができます。
非常に良い質問です。著者だけが決定的な答えを提供できると思います。 @Izkata で説明されているように、エラー処理に使用することから始めた可能性があり、その後、基本的なループにも使用するゲートが開かれたと言って、少し推測を追加します。
私の意見では、エラー処理の使用法はシステム プログラミングでは正当なものです。関数は進行するにつれてメモリを徐々に割り当て、エラーが発生した場合は goto
します。 その時点から逆の順序でリソースを解放するための適切なラベル。
そのため、最初の割り当て後にエラーが発生した場合、最後のエラー ラベルにジャンプして、1 つのリソースのみを解放します。同様に、最後の割り当て後にエラーが発生した場合、最初のエラー ラベルにジャンプしてそこから実行され、関数の最後まですべてのリソースが解放されます。エラー処理のこのようなパターンは、特にコードを変更する場合は特に注意して使用する必要があり、valgrind と単体テストを強くお勧めします。しかし、代替アプローチよりも読みやすく、保守しやすいことはほぼ間違いありません。
goto
を使用するための 1 つの黄金律 いわゆるスパゲッティコードを避けることです。各 goto
の間に線を引いてみてください ステートメントとそれぞれのラベル。交差する線がある場合は、線を越えたことになります:)。 goto
のこのような使い方 フロー制御に依存する BASIC などの言語に見られるため、非常に読みにくく、追跡が困難なバグの一般的な原因です。
単純なループを 1 つだけ実行すると、交差する行が発生しないため、依然として読みやすく保守しやすく、主にスタイルの問題になります。とはいえ、質問で示したように、言語が提供するループキーワードを使用して簡単に実行できるため、 goto
の使用を避けることをお勧めします for ループ、単に for
のため 、 do/while
または while
コンストラクトはよりエレガントに設計されています。
この例の場合、もともと非 SMP セーフな方法で記述されたコードに SMP サポートを後付けすることが目的だったのではないかと思います。 goto again;
を追加する パスは、関数を再構築するよりもはるかに単純で侵襲的ではありません.
このスタイルが好きとは言えませんが、goto
を避けるのは間違っていると思います。 イデオロギー上の理由から。 goto
の 1 つの特殊なケース 使用法 (この例とは異なります) は goto
の場所です 関数内を前進するためにのみ使用され、後退することはありません。このクラスの用法は、goto
から生じるループ構造には決してなりません。 であり、ほとんどの場合、必要な動作を実装するための最も簡単で明確な方法です (通常は、クリーンアップしてエラーが発生した場合に戻ります)。