Cプログラミング言語がまだ支配している理由

他のほとんどのテクノロジー、特にコンピューターテクノロジーよりも優れた仕事をしない限り、50年間テクノロジーが定着することはありません。Cプログラミング言語は、1972年以来生き続けており、ソフトウェア定義の世界の基本的な構成要素の1つとして今も君臨しています。

しかし、人々がテクノロジーを置き換えることに慣れていないために、テクノロジーが固執することがあります。過去数十年にわたって、他の数十の言語が登場しました。Cの優位性に異議を唱えるように明示的に設計された言語もあれば、人気の副産物としてCを横から削り取った言語もあります。

Cを交換する必要があると主張するのは難しいことではありません。プログラミング言語の研究とソフトウェア開発の実践はすべて、Cの方法よりもはるかに優れた方法があることを示唆しています。しかし、Cは何十年にもわたる研究開発を経て、まったく同じように存続しています。パフォーマンス、ベアメタルの互換性、またはユビキタスの点でこれに勝る言語は他にほとんどありません。それでも、2018年にCが有名な言語の競争にどのように対抗するかを見る価値はあります。

C対C ++

当然のことながら、CはC ++と最も一般的に比較されます。この言語は、名前自体が示すように、Cの拡張として作成されました。C++とCの違いは、誰に尋ねるかによって、広範囲または過剰として特徴付けられる可能性が あります。

C ++は、構文とアプローチがCに似ていますが、名前空間、テンプレート、例外、自動メモリ管理など、Cではネイティブに利用できない多くの真に便利な機能を提供します。最高レベルのパフォーマンスを必要とするプロジェクト(データベース、機械学習システム)は、多くの場合、これらの機能を使用してC ++で記述され、システムからパフォーマンスの低下をすべて引き出します。

さらに、C ++はCよりもはるかに積極的に拡張を続けています。次のC ++ 20は、モジュール、コルーチン、同期ライブラリ、概念など、テンプレートを使いやすくするために、さらに多くの機能を提供します。C標準の最新のリビジョンでは、ほとんど追加されておらず、下位互換性の維持に重点が置かれています。

つまり、C ++のすべてのプラスはマイナスとしても機能します。大きなもの。使用するC ++機能が多いほど、導入する複雑さが増し、結果を管理するのが難しくなります。 C ++のサブセットに限定する開発者は、最悪の落とし穴や過剰の多くを回避できます。しかし、一部のショップは、C ++の複雑さを一斉に防ぎたいと考えています。 Cに固執すると、開発者はそのサブセットに限定する必要があります。たとえば、Linuxカーネル開発チームはC ++を避けています。

C ++ではなくCを選択することは、強制されたミニマリズムを採用することで、C ++の過剰に絡まることを回避するための方法です。もちろん、C ++には、正当な理由から、高レベルの機能が豊富に用意されています。しかし、ミニマリズムが現在および将来のプロジェクト、そしてプロジェクトチームにより適している場合は、Cの方が理にかなっています。

C対Java

数十年後も、Javaはエンタープライズソフトウェア開発の定番であり、一般的には開発の定番です。最も重要なエンタープライズソフトウェアプロジェクトの多くはJavaで記述されており(Apache Software Foundationプロジェクトの大部分を含む)、Javaはエンタープライズグレードの要件を持つ新しいプロジェクトを開発するための実行可能な言語のままです。

Java構文は、CおよびC ++から多くを借用しています。ただし、Cとは異なり、Javaはデフォルトではネイティブコードにコンパイルされません。代わりに、Javaランタイム環境であるJVM、JIT(ジャストインタイム)は、Javaコードをコンパイルしてターゲット環境で実行します。適切な状況下では、JITtedJavaコードはCのパフォーマンスに近づくかそれを超える可能性があります。

Javaの背後にある「一度書けばどこでも実行できる」という哲学により、Javaプログラムは、ターゲットアーキテクチャを比較的わずかに調整するだけで実行できます。対照的に、Cは非常に多くのアーキテクチャに移植されていますが、たとえばWindowsとLinuxで正しく実行するには、特定のCプログラムをカスタマイズする必要がある場合があります。

この移植性と強力なパフォーマンスの組み合わせ、およびソフトウェアライブラリとフレームワークの大規模なエコシステムにより、Javaはエンタープライズアプリケーションを構築するための頼りになる言語とランタイムになります。

JavaがCに満たないのは、Javaが競争することを意図されていなかった領域です。つまり、金属の近くで実行するか、ハードウェアを直接操作します。 Cコードはマシンコードにコンパイルされ、プロセスによって直接実行されます。 Javaはバイトコードにコンパイルされます。バイトコードは、JVMインタープリターがマシンコードに変換する中間コードです。さらに、Javaの自動メモリ管理はほとんどの状況で祝福されますが、Cは、限られたメモリリソースを最適に使用する必要があるプログラムに適しています。

とはいえ、Javaが速度の点でCに近づく可能性のある領域がいくつかあります。JVMのJITエンジンは、プログラムの動作に基づいて実行時にルーチンを最適化し、事前コンパイルされたCでは不可能な多くのクラスの最適化を可能にします。Javaランタイムはメモリ管理を自動化しますが、一部の新しいアプリケーションはそれを回避します。たとえば、Apache Sparkは、JVMを回避するカスタムメモリ管理コードを使用して、メモリ内処理を部分的に最適化します。

C対C#および.Net

導入から約20年経った今でも、C#と.NetFrameworkはエンタープライズソフトウェアの世界の主要な部分です。C#と.Netは、マネージコードコンパイラシステムとユニバーサルランタイムであるJavaに対するMicrosoftの対応であると言われており、CとJavaの比較の多くは、CとC#/。Netにも当てはまります。

Java(およびある程度Python)と同様に、.Netは、さまざまなプラットフォーム間での移植性と、統合ソフトウェアの広大なエコシステムを提供します。.Netの世界でエンタープライズ指向の開発がどれだけ行われているかを考えると、これらは小さな利点ではありません。C#またはその他の.Net言語でプログラムを開発する場合、.Netランタイム用に作成されたツールとライブラリの世界を利用できます。 

Javaに似たもう1つの.NETの利点は、JITの最適化です。C#および.Netプログラムは、Cに従って事前にコンパイルできますが、主に.Netランタイムによってジャストインタイムでコンパイルされ、ランタイム情報で最適化されます。JITコンパイルにより、Cでは実行できない実行中の.Netプログラムのあらゆる種類のインプレース最適化が可能になります。

Cと同様に、C#と.Netは、メモリに直接アクセスするためのさまざまなメカニズムを提供します。ヒープ、スタック、およびアンマネージドシステムメモリはすべて、.NetAPIおよびオブジェクトを介してアクセスできます。また、開発者はunsafe.Netのモードを使用して、さらに優れたパフォーマンスを実現できます。

ただし、これは無料ではありません。管理対象オブジェクトとunsafeオブジェクトを任意に交換することはできず、それらの間のマーシャリングにはパフォーマンスが犠牲になります。したがって、.Netアプリケーションのパフォーマンスを最大化することは、管理対象オブジェクトと管理対象外オブジェクト間の移動を最小限に抑えることを意味します。

マネージメモリとアンマネージメモリのペナルティを支払う余裕がない場合、または.Netランタイムがターゲット環境(カーネルスペースなど)に適していない場合、またはまったく利用できない場合は、Cが最適です。必要。また、C#や.Netとは異なり、Cはデフォルトでダイレクトメモリアクセスのロックを解除します。 

C対Go

Go構文は、区切り文字としての中括弧、セミコロンで終了するステートメントなど、Cに大きく依存しています。Cに習熟している開発者は、名前空間やパッケージ管理などの新しいGo機能を考慮に入れても、通常はそれほど問題なくGoに飛び込むことができます。

読み取り可能なコードは、Goの設計目標の1つでした。開発者が、Goプロジェクトに慣れるのを簡単にし、コードベースを短時間で習得できるようにします。Cコードベースは#ifdef、プロジェクトと特定のチームの両方に固有のマクロのネズミの巣になりがちなので、理解するのが難しい場合があります。Goの構文、およびその組み込みのコードフォーマットとプロジェクト管理ツールは、この種の制度上の問題を寄せ付けないようにすることを目的としています。

Goには、ゴルーチンやチャネル、同時実行性を処理するための言語レベルのツール、コンポーネント間のメッセージパッシングなどの追加機能もあります。Cでは、そのようなものを手作業で作成するか、外部ライブラリから提供する必要がありますが、Goはそれらをすぐに提供するため、それらを必要とするソフトウェアの構築がはるかに簡単になります。

Goが内部でCと最も異なるのは、メモリ管理です。Goオブジェクトは自動的に管理され、デフォルトでガベージコレクションされます。ほとんどのプログラミングジョブでは、これは非常に便利です。しかし、それはまた、メモリの決定論的な処理を必要とするプログラムを書くのが難しくなることを意味します。

Goには、unsafe型を使用した任意のメモリの読み取りや書き込みなど、Goの型処理の安全性の一部を回避するためのパッケージが含まれていますPointer。ただしunsafe、それを使用して作成されたプログラムは「移植性がなく、Go1互換性ガイドラインによって保護されていない可能性がある」という警告が表示されます。

Goは、コマンドラインユーティリティやネットワークサービスなどのプログラムを構築するのに適しています。これは、このようなきめ細かい操作が必要になることはめったにないためです。ただし、低レベルのデバイスドライバー、カーネルスペースオペレーティングシステムコンポーネント、およびメモリのレイアウトと管理を厳密に制御する必要があるその他のタスクは、Cで作成するのが最適です。

C対錆

ある意味で、Rustは、CおよびC ++によって作成されたメモリ管理の難問、およびこれらの言語の他の多くの欠点への対応です。Rustはネイティブマシンコードにコンパイルされるため、パフォーマンスに関してはCと同等と見なされます。ただし、デフォルトでは、メモリの安全性がRustの主なセールスポイントです。

Rustの構文とコンパイルのルールは、開発者が一般的なメモリ管理の失敗を回避するのに役立ちます。プログラムにRust構文と交差するメモリ管理の問題がある場合、プログラムは単にコンパイルされません。この言語、特にそのようなバグに十分な余地を提供するCのような言語の初心者は、コンパイラをなだめる方法を学ぶためにRust教育の最初のフェーズを費やします。しかし、Rustの支持者は、この短期的な苦痛には長期的な見返りがあると主張しています。つまり、速度を犠牲にしない、より安全なコードです。

錆はまた、そのツールでCを改善します。プロジェクトとコンポーネントの管理は、Goと同様に、デフォルトでRustに付属しているツールチェーンの一部です。パッケージを管理し、プロジェクトフォルダーを整理し、Cではせいぜいアドホックである他の非常に多くのことを処理するためのデフォルトの推奨される方法があります。各プロジェクトとチームはそれらを異なる方法で処理します。

それでも、Rustの利点として宣伝されていることは、C開発者にとっては1つのようには思えないかもしれません。Rustのコンパイル時の安全機能を無効にすることはできないため、最も些細なRustプログラムでさえ、Rustのメモリ安全制限に準拠する必要があります。Cはデフォルトでは安全性が低い場合がありますが、必要に応じてはるかに柔軟で寛容です。

もう1つの考えられる欠点は、Rust言語のサイズです。Cは、標準ライブラリを考慮しても、機能が比較的少ないです。Rust機能セットは広大であり、成長を続けています。C ++と同様に、Rust機能セットが大きいほど、より強力ですが、より複雑になります。Cは小さい言語ですが、精神的にモデル化するのがはるかに簡単なので、Rustがやり過ぎになるプロジェクトにおそらく適しています。

C対Python

最近、話がソフトウェア開発についてであるときはいつでも、Pythonは常に会話に入っているようです。結局のところ、Pythonは「すべてにとって2番目に優れた言語」であり、間違いなく最も用途の広い言語の1つであり、何千ものサードパーティライブラリが利用可能です。

Pythonが強調していること、そしてそれがCと最も異なる点は、実行速度よりも開発速度を優先することです。 Cなどの別の言語で組み立てるのに1時間かかる可能性のあるプログラムは、Pythonで数分で組み立てられる可能性があります。反対に、そのプログラムはCで実行するのに数秒かかるかもしれませんが、Pythonで実行するのに1分かかるかもしれません。 (大まかな目安:Pythonプログラムは、通常、Cプログラムよりも1桁遅く実行されます。)しかし、最新のハードウェアでの多くのジョブでは、Pythonは十分に高速であり、それがその普及の鍵となっています。

もう1つの大きな違いは、メモリ管理です。PythonプログラムはPythonランタイムによって完全にメモリ管理されているため、開発者はメモリの割り当てと解放の要点について心配する必要はありません。しかしここでも、開発者の使いやすさは実行時のパフォーマンスを犠牲にしてもたらされます。Cプログラムを作成するには、メモリ管理に細心の注意を払う必要がありますが、結果として得られるプログラムは、純粋なマシン速度のゴールドスタンダードになることがよくあります。

ただし、PythonとCは深いつながりを共有しています。つまり、参照PythonランタイムはCで記述されています。これにより、PythonプログラムはCとC ++で記述されたライブラリをラップできます。機械学習など、サードパーティライブラリのPythonエコシステムの重要なチャンクには、Cコードがコアに含まれています。

開発の速度が実行の速度よりも重要であり、プログラムの実行可能な部分のほとんどをスタンドアロンコンポーネントに分離できる場合(コード全体に分散するのではなく)、純粋なPythonまたはPythonとCライブラリの組み合わせのいずれかが作成されますCだけよりも良い選択です。そうでなければ、Cはまだ支配します。