本当の理由は、一方ではCとC ++、もう一方ではJavaとC#(ほんの2、3の例のみ)の意図の根本的な違いに帰着します。歴史的な理由から、ここでの議論の多くはC ++ではなくCについて述べていますが、(おそらくご存知のように)C ++はCのかなり直接的な子孫なので、Cについて言うことはC ++にも等しく当てはまります。
UNIXの大部分は忘れられていますが(その存在は時には否定されることもあります)、UNIXの最初のバージョンはアセンブリ言語で書かれていました。Cの当初の目的の大部分は(もしそうでないとしても)、UNIXをアセンブリ言語から高レベル言語に移植することでした。意図の一部は、可能な限り多くのオペレーティングシステムを高レベルの言語で記述すること、またはアセンブリ言語で書かなければならない量を最小限に抑えるために他の方向からそれを調べることでした。
それを実現するために、Cは、アセンブリ言語とほぼ同じレベルのハードウェアへのアクセスを提供する必要がありました。PDP-11は(一例として)I / Oレジスタを特定のアドレスにマップしました。たとえば、1つのメモリ位置を読み取って、システムコンソールでキーが押されたかどうかを確認します。読み取りを待機しているデータがあるときに、その場所に1ビットが設定されました。次に、指定された別の場所から1バイトを読み取り、押されたキーのASCIIコードを取得します。
同様に、一部のデータを印刷する場合は、指定した別の場所を確認し、出力デバイスの準備ができたら、指定した別の場所にデータを書き込みます。
このようなデバイスのドライバーの作成をサポートするために、Cでは、整数型を使用して任意の場所を指定し、ポインターに変換して、その場所をメモリ内で読み書きできます。
もちろん、これにはかなり深刻な問題があります。地球上のすべてのマシンのメモリが1970年代初期のPDP-11と同じようにレイアウトされているわけではありません。そのため、その整数を受け取ってポインターに変換し、そのポインターを介して読み取りまたは書き込みを行うと、取得しようとしているものについて誰も合理的な保証を提供できません。わかりやすい例として、読み取りと書き込みはハードウェア内の別々のレジスタにマッピングされる可能性があるため、何かを書き込んでからそれを読み直そうとすると、(通常のメモリとは対照的に)読んだ内容が書き込んだ内容と一致しない場合があります。
残る可能性がいくつかあります。
- 考えられるすべてのハードウェアへのインターフェースを定義します。何らかの方法でハードウェアとやり取りするために、読み取りまたは書き込みが必要なすべての場所の絶対アドレスを指定します。
- そのレベルのアクセスを禁止し、そのようなことをしたい人はだれでもアセンブリ言語を使用する必要があることを宣言します。
- 人々がそれを行うことを許可しますが、ターゲットとするハードウェアのマニュアルを読んで(たとえば)、使用しているハードウェアに合うようにコードを書くことは彼らに任せます。
これらのうち、1は十分に馬鹿げているように思われるので、さらに議論する価値はほとんどありません。2は基本的に言語の基本的な意図を捨てています。これにより、3番目のオプションは、本質的に合理的に検討できる唯一のオプションとなります。
かなり頻繁に発生する別のポイントは、整数型のサイズです。Cは、int
アーキテクチャによって提案された自然なサイズであるはずの「位置」を取ります。したがって、32ビットVAXをプログラミングする場合、int
おそらく32ビットである必要がありますが、36ビットUnivacをプログラミングする場合、int
おそらく36ビット(など)である必要があります。サイズが8ビットの倍数であることが保証されている型のみを使用して36ビットコンピューターのオペレーティングシステムを記述することは、おそらく合理的ではありません(不可能な場合もあります)。たぶん私は表面的なだけかもしれませんが、36ビットマシン用のOSを書いているなら、おそらく36ビットタイプをサポートする言語を使いたいと思うようです。
言語の観点から、これはさらに未定義の動作につながります。32ビットに収まる最大値を取得した場合、1を追加するとどうなりますか?通常の32ビットハードウェアでは、ロールオーバー(または、何らかのハードウェア障害が発生する可能性があります)一方、36ビットのハードウェアで実行されている場合は、追加するだけです。言語がオペレーティングシステムの記述をサポートする場合、どちらの動作も保証できません。型のサイズとオーバーフローの動作の両方を異なるように許可する必要があります。
JavaとC#はそれらすべてを無視できます。オペレーティングシステムの記述をサポートすることを目的としていません。それらを使用すると、いくつかの選択肢があります。1つは、ハードウェアが要求するものをサポートするようにすることです。8、16、32、64ビットのタイプを要求するため、それらのサイズをサポートするハードウェアを構築するだけです。他の明らかな可能性は、基礎となるハードウェアが何を望んでいるかに関係なく、言語が彼らが望む環境を提供する他のソフトウェアの上でのみ動作することです。
ほとんどの場合、これは実際にはどちらかまたは両方の選択肢ではありません。むしろ、多くの実装は両方を少し実行します。通常、オペレーティングシステムで実行されているJVMでJavaを実行します。多くの場合、OSはCで、JVMはC ++で記述されています。JVMがARM CPUで実行されている場合、CPUにARMのJazelle拡張機能が含まれており、ハードウェアをJavaのニーズにより近づけるため、ソフトウェアで実行する必要が少なくなり、Javaコードの実行速度が速くなりますとにかくゆっくり)。
概要
CとC ++には未定義の動作があります。これは、誰もが意図したことを実行できる許容可能な代替手段を定義していないためです。C#とJavaは別のアプローチを取りますが、そのアプローチはCとC ++の目標に(もしあれば)不十分に適合します。特に、どちらも、arbitrarily意的に選択されたほとんどのハードウェア上でシステムソフトウェア(オペレーティングシステムなど)を作成するための合理的な方法を提供していないようです。どちらも通常、既存のシステムソフトウェア(通常はCまたはC ++で記述されている)が提供する機能に依存してジョブを実行します。