私がCを学んだとき、先生は一日中私に言った:「gotoを使用しないでください、それは悪い習慣です、それは醜く、危険です!」等々。
それでは、なぜ一部のカーネルプログラマはgoto
を使用するのでしょうか。たとえば、 この関数では を使用します。
while(condition) {}
または
do {} while(condition);
理解できません。場合によっては、while
/do
-while
の代わりにgotoを使用する方が良いですか?もしそうなら、なぜですか?
この例の場合、それはSMPサポートを元々SMPセーフでない方法で記述されたコードに改造することに関するものだと思います。 goto again;
パスを追加することは、関数を再構築するよりもはるかに単純で侵襲性が低くなります。
私はこのスタイルが好きだとは言えませんが、イデオロギー上の理由からgoto
を避けるのは見当違いだと思います。 goto
の使用法の特殊なケースの1つ(この例とは異なります)は、goto
が関数内で前方に移動するためにのみ使用され、後方には使用されない場合です。このクラスの使用法では、goto
からループ構造が発生することはなく、ほとんどの場合、必要な動作を実装するための最も単純で明確な方法です(通常、クリーンアップしてエラーが返されます)。
歴史的文脈:ダイクストラがGotoは有害と見なされるを書いたことを覚えておく必要があります1968年、多くのプログラマがgoto
を 構造化プログラミング (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;
生成されたコードは基本的に同じなので、インデントのような表記上の問題と考えることができます。