GNU/Linux >> Linux の 問題 >  >> Linux

一部のカーネル プログラマーが単純な while ループの代わりに goto を使用するのはなぜですか?

歴史的背景: ダイクストラが 後藤は有害と見なされる と書いたことを覚えておく必要があります。 多くのプログラマーが goto を使用していた 1968 年に 構造化プログラミングの代替として (ifwhilefor など)。

それから 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 から生じるループ構造には決してなりません。 であり、ほとんどの場合、必要な動作を実装するための最も簡単で明確な方法です (通常は、クリーンアップしてエラーが発生した場合に戻ります)。


Linux
  1. `ifs=の代わりに`whileIfs=Read`が頻繁に使用されるのはなぜですか。読みながら..`?

  2. Linux –なぜSuだけでなくSuを使用するのですか?

  3. $((Expr))の代わりに$ [Expr]を使用しますか?

  1. Ubuntu 14.04 Ltsが非ltsカーネルバージョンを使用するのはなぜですか?

  2. CentOS/RHEL でマルチパス デバイスを削除する際の「map in use」エラー

  3. 一部のカーネル プログラマーが単純な while ループの代わりに goto を使用するのはなぜですか?

  1. glibc の fclose(NULL) がエラーを返す代わりにセグメンテーション違反を引き起こすのはなぜですか?

  2. パターンの前にテキストを追加する際に、大文字と小文字を区別しないで sed を使用する

  3. カーネルプログラミングで unsigned int の代わりに u8 u16 u32 u64 が使用される理由