ここでの回答に追加するには、これと組み合わせて反対の質問を検討する価値があると思います。そもそもなぜCはフォールスルーを許可したのですか?
もちろん、どのプログラミング言語も次の 2 つの目標に役立ちます。
- コンピューターに指示を出します。
- プログラマーの意図の記録を残します。
したがって、プログラミング言語の作成は、これら 2 つの目標をどのように達成するのが最善かのバランスを取ることになります。一方では、コンピューター命令 (機械語、IL のようなバイトコード、命令が実行時に解釈されるもの) への変換が容易になればなるほど、コンパイルまたは解釈のプロセスが効率的で信頼性が高くなり、より効率的になります。出力をコンパクトに。極端に言えば、この目標は、アセンブリ、IL、または生のオペコードでさえも書くことになります。なぜなら、最も簡単なコンパイルは、コンパイルがまったくない場所だからです。
逆に言えば、言語がプログラマーの意図を表現する手段ではなく、その意図を表現する言語であるほど、プログラムを作成するときも保守するときも、プログラムはより理解しやすくなります。
今では、それをブロックswitch
の同等のチェーンまたは類似のものに変換することによって常にコンパイルできましたが、値を取得し、それからオフセットを計算する特定の一般的なアセンブリパターンにコンパイルできるように設計されました (テーブルを検索するかどうかにかかわらず)if-else
値の完全なハッシュ、または値の実際の算術演算によってインデックス付けされます*)。この時点で注目に値するのは、現在、C# のコンパイルがswitch
同等のに変わることもあればif-else
、ハッシュ ベースのジャンプ アプローチを使用することもあります (C、C++、および同等の構文を持つ他の言語でも同様です)。
この場合、フォールスルーを許可する正当な理由が 2 つあります。
いずれにせよ、それは自然に発生します。ジャンプ テーブルを一連の命令に組み込み、以前の命令のバッチの 1 つに何らかのジャンプまたはリターンが含まれていない場合、実行は自然に次のバッチに進みます。switch
マシン コードを使用して、C を使用してジャンプ テーブルに変換した場合、フォールスルーを許可することは「ただ起こる」ことでした。
アセンブリで記述したコーダーは、すでに同等の作業に慣れていました。アセンブリでジャンプ テーブルを手動で記述する場合、特定のコード ブロックが戻り値で終了するか、テーブル外へのジャンプで終了するか、そのまま続行するかを考慮する必要があります。次のブロックへ。そのため、必要に応じてコーダーに明示的に追加させることはbreak
、コーダーにとっても「自然」でした。
したがって、当時は、生成されたマシン コードとソース コードの表現力の両方に関連するコンピューター言語の 2 つの目標のバランスを取ることが妥当な試みでした。
40 年経った今でも、状況はまったく同じではありません。それにはいくつかの理由があります。
- 今日の C 言語のコーダーは、アセンブリの経験がほとんど、またはまったくない場合があります。他の多くの C スタイル言語のコーダーは、(特に Javascript!) する可能性はさらに低くなります。「アセンブリから慣れ親しんだもの」という概念は、もはや関係ありません。
- 最適化の改善は、アプローチが最も効率的である可能性が高いと見なさ
switch
れif-else
たために変更されるか、そうでなければジャンプ テーブル アプローチの特に難解なバリアントに変更される可能性が高いことを意味します。高レベルのアプローチと低レベルのアプローチの間のマッピングは、以前ほど強力ではありません。
- 経験上、フォールスルーは標準ではなく少数のケースである傾向があることが示されています (Sun のコンパイラの調査では、ブロックの 3% が
switch
同じブロックで複数のラベル以外のフォールスルーを使用していることがわかりました。ここでのケースは、この 3% が実際には通常よりもはるかに高かったことを意味します)。したがって、研究された言語は、一般的なものよりも珍しいものをより容易に提供します。
- 経験上、フォールスルーが誤って実行された場合と、コードを保守している誰かが正しいフォールスルーを見落とした場合の両方で、フォールスルーが問題の原因になる傾向があることが示されています。この後者は、フォールスルーに関連するバグへの微妙な追加です。コードに完全にバグがない場合でも、フォールスルーが問題を引き起こす可能性があるためです。
これらの最後の 2 つのポイントに関連して、K&R の最新版からの次の引用を検討してください。
あるケースから別のケースへのフォールスルーは堅牢ではなく、プログラムが変更されたときに崩壊する傾向があります。1 つの計算に複数のラベルを付ける場合を除き、フォールスルーは慎重に使用し、コメントを付ける必要があります。
適切な形式の問題として、論理的には不要ですが、最後のケース (ここではデフォルト) の後に区切りを入れます。ある日、最後に別のケースが追加されたときに、この防御的なプログラミングのビットがあなたを救います。
したがって、馬の口から、C でのフォールスルーは問題です。フォールスルーを常にコメントで文書化することは良い習慣と考えられています。これは、何か異常なことをした場所を文書化する必要があるという一般原則の適用です。実際には正しいのに、初心者のバグがあります。
考えてみると、次のようにコーディングします。
switch(x)
{
case 1:
foo();
/* FALLTHRU */
case 2:
bar();
break;
}
コードでフォールスルーを明示的にするために何かを追加していますが、それはコンパイラによって検出できる (または存在しないことを検出できる) ものではありません。
そのため、C# のフォールスルーで明示的でなければならないという事実は、他の C スタイルの言語で適切に記述した人々にペナルティを追加するものではありません。 /p>
最後に、goto
here の使用は、C や他の同様の言語では既に標準になっています。
switch(x)
{
case 0:
case 1:
case 2:
foo();
goto below_six;
case 3:
bar();
goto below_six;
case 4:
baz();
/* FALLTHRU */
case 5:
below_six:
qux();
break;
default:
quux();
}
この種の場合、前のブロックに 1 をもたらす値以外の値に対して実行されるコードにブロックを含めたい場合は、既に を使用する必要がありますgoto
。(もちろん、さまざまな条件でこれを回避する手段と方法がありますが、それはこの質問に関連するほぼすべてに当てはまります)。そのため、C# は、 で複数のコード ブロックをヒットする必要がある 1 つの状況に対処するために、既に通常の方法を構築し、switch
フォールスルーもカバーするように一般化しました。case
また、C では新しいラベルを追加する必要がありますが、C# ではラベルとして使用できるため、両方のケースがより便利で自己文書化されました。C# では、ラベルを削除して、何をしているのか明確なbelow_six
ラベルを使用できます。goto case 5
(追加する必要もありますbreak
についてはdefault
、上記の C コードを C# コードではないことを明確にするために省略しました)。
したがって、要約すると:
- C# は、40 年前の C コードのように最適化されていないコンパイラの出力に直接関係しなくなりました (最近の C もそうではありません)。
- C# は、単に Implicit を持っているだけでなく、C との互換性を維持しており
break
、同様の言語に精通しているユーザーによる言語の学習が容易になり、移植が容易になります。
- C# は、過去 40 年間、問題の原因として十分に文書化されてきたバグや誤解されたコードの潜在的な原因を取り除きます。
- C# では、C を使用した既存のベスト プラクティス (ドキュメント フォール スルー) がコンパイラによって適用可能になります。
- C# は、より明示的なコードを使用するケースを異常なケースにし、自動的に記述したコードを使用する通常のケースを作成します。
- C# は、C で使用されているのと同じ
goto
ベースのアプローチを使用して、異なるcase
ラベルから同じブロックをヒットします。他のケースに一般化するだけです。
- C# では、ステートメントがラベルとして機能
goto
できるようにすることで、C よりもベースのアプローチがより便利で明確になります。case
全体として、かなり合理的な設計上の決定
*BASIC のいくつかの形式は、GOTO (x AND 7) * 50 + 240
脆弱であり、したがって禁止の特に説得力のあるケースであるgoto
一方で、以下のようなことを行うことを可能にします。これは、手動で維持する必要があるものではなく、コンパイルの結果である場合にはるかに合理的です。特に、ダフのデバイスの実装は、同等のマシン コードまたは IL に適しています。これは、多くの場合、命令の各ブロックがnop
フィラーの追加を必要とせずに同じ長さになるためです。
†ダフの装置は、妥当な例外として、ここでも登場します。それと同様のパターンで操作の繰り返しがあるという事実は、その旨の明示的なコメントがなくても、フォールスルーの使用を比較的明確にするのに役立ちます。