これらすべての年月を経ても、世界は依然としてCプログラミングによって支えられています

公開: 2022-03-11

今日存在するCプロジェクトの多くは、数十年前に開始されました。

UNIXオペレーティングシステムの開発は1969年に開始され、そのコードは1972年にCで書き直されました。C言語は、UNIXカーネルコードをアセンブリから高級言語に移行するために実際に作成されました。これにより、同じタスクをより少ないコード行で実行できます。 。

Oracleデータベースの開発は1977年に開始され、そのコードは1983年にアセンブリからCに書き直されました。これは、世界で最も人気のあるデータベースの1つになりました。

1985年にWindows1.0がリリースされました。 Windowsのソースコードは公開されていませんが、そのカーネルはほとんどCで記述されており、一部はアセンブリに含まれていると言われています。 Linuxカーネルの開発は1991年に始まり、Cでも書かれています。翌年、GNUライセンスの下でリリースされ、GNUオペレーティングシステムの一部として使用されました。 GNUオペレーティングシステム自体はCおよびLispプログラミング言語を使用して開始されたため、そのコンポーネントの多くはCで記述されています。

しかし、Cプログラミングは、今日ほど多くのプログラミング言語がなかった数十年前に開始されたプロジェクトに限定されていません。 多くのCプロジェクトは今日でも開始されています。 それにはいくつかの正当な理由があります。

世界はどのようにCによって動かされていますか?

高水準言語の普及にもかかわらず、Cは世界に力を与え続けています。 以下は、数百万人が使用し、C言語でプログラムされているシステムの一部です。

マイクロソフトウィンドウズ

MicrosoftのWindowsカーネルは、ほとんどがCで開発されており、一部はアセンブリ言語で開発されています。 何十年もの間、市場シェアの約90%を占める世界で最も使用されているオペレーティングシステムは、Cで記述されたカーネルを利用してきました。

Linux

LinuxもほとんどCで書かれており、一部はアセンブリに含まれています。 世界で最も強力な500台のスーパーコンピューターの約97%がLinuxカーネルを実行しています。 また、多くのパソコンで使用されています。

マック

OS Xカーネルは主にCで記述されているため、MacコンピューターもCを搭載しています。WindowsやLinuxコンピューターと同様に、MacのすべてのプログラムとドライバーはCを搭載したカーネルで実行されます。

モバイル

iOS、Android、およびWindows PhoneのカーネルもCで記述されています。これらは、既存のMac OS、Linux、およびWindowsカーネルをモバイルに適応させたものです。 そのため、毎日使用するスマートフォンはCカーネルで実行されています。

Cで記述されたオペレーティングシステムカーネル

データベース

Oracle Database、MySQL、MS SQL Server、PostgreSQLなど、世界で最も人気のあるデータベースはCでコーディングされています(最初の3つは実際にはCとC ++の両方です)。

データベースは、金融、政府、メディア、エンターテインメント、電気通信、健康、教育、小売、ソーシャルネットワーク、Webなど、あらゆる種類のシステムで使用されています。

Cを利用したデータベース

3D映画

3Dムービーは、通常CおよびC++で記述されたアプリケーションを使用して作成されます。 これらのアプリケーションは、大量のデータを処理し、1秒あたり多くの計算を実行するため、非常に効率的かつ高速である必要があります。 それらが効率的であるほど、アーティストやアニメーターが映画のショットを生成するのにかかる時間は短くなり、会社はより多くのお金を節約できます。

組み込みシステム

ある日目を覚まして買い物に行くと想像してみてください。 あなたを目覚めさせる目覚まし時計はおそらくCでプログラムされています。それからあなたはあなたの朝食を作るためにあなたの電子レンジかコーヒーメーカーを使います。 これらも組み込みシステムであるため、おそらくCでプログラムされています。朝食を食べている間、テレビまたはラジオの電源を入れます。 これらもCを搭載した組み込みシステムです。リモコンでガレージのドアを開けると、Cでプログラムされている可能性が最も高い組み込みシステムも使用しています。

それからあなたはあなたの車に乗り込みます。 次の機能がある場合は、Cでプログラムされています。

  • オートマチックトランスミッション
  • タイヤ空気圧検出システム
  • センサー(酸素、温度、オイルレベルなど)
  • シートとミラー設定用のメモリ。
  • ダッシュボードディスプレイ
  • アンチロックブレーキ
  • 自動安定性制御
  • クルーズコントロール
  • 気候制御
  • チャイルドプルーフロック
  • キーレスエントリー
  • 暖房付きシート
  • エアバッグ制御

あなたは店に着き、車を駐車し、自動販売機に行ってソーダを手に入れます。 彼らはこの自動販売機をプログラムするためにどの言語を使用しましたか? おそらくC。それからあなたは店で何かを買う。 レジもCでプログラムされています。クレジットカードで支払う場合はどうなりますか? ご想像のとおり、クレジットカードリーダーはおそらくCでプログラムされています。

これらのデバイスはすべて組み込みシステムです。 それらは、組み込みデバイス上でファームウェアとも呼ばれるプログラムを実行しているマイクロコントローラー/マイクロプロセッサーを内部に備えた小さなコンピューターのようなものです。 そのプログラムは、キーの押下を検出してそれに応じて動作し、ユーザーに情報を表示する必要があります。 たとえば、目覚まし時計はユーザーと対話して、ユーザーが押しているボタンと、場合によってはボタンが押されている時間を検出し、それに応じてデバイスをプログラムし、すべてユーザーに関連情報を表示する必要があります。 たとえば、自動車のアンチロックブレーキシステムは、タイヤの突然のロックを検出し、ブレーキへの圧力を短時間解放してロックを解除し、それによって制御不能な横滑りを防ぐことができなければなりません。 これらの計算はすべて、プログラムされた組み込みシステムによって行われます。

組み込みシステムで使用されるプログラミング言語はブランドごとに異なりますが、柔軟性、効率、パフォーマンス、およびハードウェアへの近さという言語の機能により、最も一般的にはC言語でプログラミングされます。

組み込みシステムはしばしばCで書かれています

なぜCプログラミング言語がまだ使われているのですか?

今日、さまざまな種類のプロジェクトで開発者がCよりも生産性を高めることができる多くのプログラミング言語があります。 JSON、XML、UI、Webページ、クライアントリクエスト、データベース接続、メディア操作などの操作を簡素化する、はるかに大きな組み込みライブラリを提供する高級言語があります。

しかし、それにもかかわらず、Cプログラミングが長期間アクティブであり続けると信じる理由はたくさんあります。

プログラミング言語では、1つのサイズですべてに対応できるわけではありません。 特定のアプリケーションでCが無敵であり、ほぼ必須である理由は次のとおりです。

移植性と効率性

Cはほとんど移植可能なアセンブリ言語です。 既存のプロセッサアーキテクチャでほぼ普遍的に利用可能ですが、可能な限りマシンに近いです。 ほぼすべての既存のアーキテクチャに対して、少なくとも1つのCコンパイラがあります。 そして今日では、最新のコンパイラーによって生成された高度に最適化されたバイナリーのため、手書きのアセンブリーでそれらの出力を改善することは簡単な作業ではありません。

これがその移植性と効率性であり、「他のプログラミング言語のコンパイラー、ライブラリー、およびインタープリターは、多くの場合Cで実装されます」。 Python、Ruby、PHPなどのインタプリタ言語の主要な実装はCで記述されています。これは、他の言語がマシンと通信するためにコンパイラによっても使用されます。 たとえば、CはEiffelとForthの基礎となる中間言語です。 つまり、サポートされるすべてのアーキテクチャーのマシンコードを生成する代わりに、それらの言語のコンパイラーは中間のCコードを生成するだけで、Cコンパイラーがマシンコードの生成を処理します。

Cは、開発者間のコミュニケーションの共通語にもなっています。 DropboxエンジニアリングマネージャーでCprogramming.comの作成者であるAlexAllainは、次のように述べています。

Cは、ほとんどの人が快適に使用できる方法でプログラミングの一般的なアイデアを表現するための優れた言語です。 さらに、Cで使用される多くの原則(たとえば、コマンドラインパラメーターのargcargv 、ループ構造と変数タイプ)は、学習する他の多くの言語で表示されるため、話すことができます。たとえ彼らがあなたの両方に共通の方法でCを知らなくても人々に。

メモリ操作

任意のメモリアドレスアクセスとポインタ演算は、Cをシステムプログラミング(オペレーティングシステムと組み込みシステム)に完全に適合させる重要な機能です。

ハードウェア/ソフトウェアの境界では、コンピューターシステムとマイクロコントローラーが周辺機器とI/Oピンをメモリアドレスにマップします。 システムアプリケーションは、世界と通信するために、これらのカスタムメモリ位置の読み取りと書き込みを行う必要があります。 したがって、任意のメモリアドレスを操作するCの機能は、システムプログラミングに不可欠です。

マイクロコントローラは、たとえば、メモリアドレス0x40008000のバイトが、アドレス0x40008001のビット番号4が設定されるたびに、ユニバーサル非同期受信機/送信機(またはUART、周辺機器と通信するための一般的なハードウェアコンポーネント)によって送信されるように設計できます。 1に設定すると、そのビットを設定すると、ペリフェラルによって自動的に設定が解除されます。

これは、そのUARTを介してバイトを送信するC関数のコードになります。

 #define UART_BYTE *(char *)0x40008000 #define UART_SEND *(volatile char *)0x40008001 |= 0x08 void send_uart(char byte) { UART_BYTE = byte; // write byte to 0x40008000 address UART_SEND; // set bit number 4 of address 0x40008001 }

関数の最初の行は次のように展開されます。

 *(char *)0x40008000 = byte;

この行は、値0x40008000charへのポインターとして解釈し、次にそのポインターを逆参照(左端の*演算子を使用)し、最後にその逆参照ポインターにbyte値を割り当てるようにコンパイラーに指示します。 つまり、可変byteの値をメモリアドレス0x40008000に書き込みます。

次の行は次のように展開されます。

 *(volatile char *)0x40008001 |= 0x08;

この行では、アドレス0x40008001の値と値0x08 (バイナリでは00001000 、つまりビット番号4では1)に対してビット単位のOR演算を実行し、結果をアドレス0x40008001に保存します。 言い換えると、アドレス0x40008001にあるバイトのビット4を設定します。 また、アドレス0x40008001の値は揮発性であることを宣言します。 これは、この値がコードの外部のプロセスによって変更される可能性があることをコンパイラーに通知するため、コンパイラーは、アドレスに書き込んだ後、そのアドレスの値について何も想定しません。 (この場合、このビットは、ソフトウェアによって設定された直後にUARTハードウェアによって設定されません。)この情報は、コンパイラのオプティマイザにとって重要です。 たとえば、値が揮発性であることを指定せずにforループ内でこれを行った場合、コンパイラは、この値が設定された後は変更されないと想定し、最初のループの後にコマンドの実行をスキップする可能性があります。

リソースの決定論的使用

システムプログラミングが信頼できない一般的な言語機能は、ガベージコレクション、または一部の組み込みシステムの動的割り当てです。 組み込みアプリケーションは、時間とメモリリソースが非常に限られています。 これらは、ガベージコレクターへの非決定論的な呼び出しを行うことができないリアルタイムシステムでよく使用されます。 また、メモリ不足のために動的割り当てを使用できない場合は、Cポインタで許可されているように、カスタムアドレスにデータを配置するなど、メモリ管理の他のメカニズムを使用することが非常に重要です。 動的割り当てとガベージコレクションに大きく依存する言語は、リソースが制限されたシステムには適していません。

コードサイズ

Cの実行時間は非常に短いです。 また、そのコードのメモリフットプリントは、他のほとんどの言語よりも小さくなっています。

たとえば、C ++と比較すると、組み込みデバイスに送られるCで生成されたバイナリは、同様のC++コードで生成されたバイナリの約半分のサイズです。 その主な原因の1つは、例外のサポートです。

例外は、CよりもC ++によって追加された優れたツールであり、トリガーされてスマートに実装されていない場合、実行時間のオーバーヘッドは実質的にありません(ただし、コードサイズが大きくなります)。

C++での例を見てみましょう。

 // Class A declaration. Methods defined somewhere else; class A { public: A(); // Constructor ~A(); // Destructor (called when the object goes out of scope or is deleted) void myMethod(); // Just a method }; // Class B declaration. Methods defined somewhere else; class B { public: B(); // Constructor ~B(); // Destructor void myMethod(); // Just a method }; // Class C declaration. Methods defined somewhere else; class C { public: C(); // Constructor ~C(); // Destructor void myMethod(); // Just a method }; void myFunction() { A a; // Constructor aA() called. (Checkpoint 1) { B b; // Constructor bB() called. (Checkpoint 2) b.myMethod(); // (Checkpoint 3) } // b.~B() destructor called. (Checkpoint 4) { C c; // Constructor cC() called. (Checkpoint 5) c.myMethod(); // (Checkpoint 6) } // c.~C() destructor called. (Checkpoint 7) a.myMethod(); // (Checkpoint 8) } // a.~A() destructor called. (Checkpoint 9)

AB 、およびCクラスのメソッドは、別の場所(たとえば、他のファイル)で定義されています。 したがって、コンパイラはそれらを分析できず、例外をスローするかどうかを知ることができません。 したがって、コンストラクタ、デストラクタ、またはその他のメソッド呼び出しからスローされた例外を処理する準備をする必要があります。 デストラクタはスローしないでください(非常に悪い習慣です)が、ユーザーはとにかくスローするか、例外をスローする関数またはメソッドを(明示的または暗黙的に)呼び出すことによって間接的にスローする可能性があります。

myFunctionのいずれかの呼び出しが例外をスローした場合、スタックアンワインドメカニズムは、すでに構築されているオブジェクトのすべてのデストラクタを呼び出すことができる必要があります。 スタックアンワインドメカニズムの1つの実装では、この関数からの最後の呼び出しのリターンアドレスを使用して、例外をトリガーした呼び出しの「チェックポイント番号」を確認します(これは簡単な説明です)。 これは、次のように、関数の本体から例外がスローされた場合にスタックの巻き戻しに使用される補助的な自動生成関数(ルックアップテーブルの一種)を利用することによって行われます。

 // Possible autogenerated function void autogeneratedStackUnwindingFor_myFunction(int checkpoint) { switch (checkpoint) { // case 1 and 9: do nothing; case 3: b.~B(); goto destroyA; // jumps to location of destroyA label case 6: c.~C(); // also goes to destroyA as that is the next line destroyA: // label case 2: case 4: case 5: case 7: case 8: a.~A(); } }

チェックポイント1および9から例外がスローされた場合、オブジェクトを破棄する必要はありません。 チェックポイント3の場合、 baを破棄する必要があります。 チェックポイント6の場合、 caを破棄する必要があります。 すべての場合において、破壊命令は尊重されなければなりません。 チェックポイント2、4、5、7、および8の場合、オブジェクトaのみを破棄する必要があります。

この補助関数は、コードにサイズを追加します。 これは、C ++がCに追加するスペースオーバーヘッドの一部です。多くの組み込みアプリケーションは、この余分なスペースを買う余裕がありません。 したがって、組み込みシステム用のC ++コンパイラには、例外を無効にするフラグが付いていることがよくあります。 標準テンプレートライブラリはエラーを通知するために例外に大きく依存しているため、C++で例外を無効にすることは無料ではありません。 この変更されたスキームを例外なく使用するには、C ++開発者が考えられる問題を検出したり、バグを見つけたりするためのトレーニングがさらに必要です。

そして、私たちはC ++について話しています。この言語の原則は、「使用しないものに対してはお金を払わない」というものです。 バイナリサイズのこの増加は、非常に便利であるが組み込みシステムでは提供できない他の機能でオーバーヘッドを追加する他の言語ではさらに悪化します。 Cではこれらの追加機能を使用できませんが、他の言語よりもはるかにコンパクトなコードフットプリントが可能になります。

Cを学ぶ理由

Cは学ぶのが難しい言語ではないので、Cを学ぶことによるすべての利点は非常に安くなります。 それらの利点のいくつかを見てみましょう。

リンガ・フランカ

すでに述べたように、Cは開発者にとって共通語です。 本やインターネットでの新しいアルゴリズムの多くの実装は、最初に(または唯一)著者によってCで利用可能になりました。 これにより、実装に最大限の移植性がもたらされます。 プログラマーがCの非常に基本的な概念を知らなかったために、インターネット上でCアルゴリズムを他のプログラミング言語に書き直すのに苦労しているのを見てきました。

Cは古くて普及している言語であるため、Cで記述されたあらゆる種類のアルゴリズムをWeb上で見つけることができることに注意してください。 したがって、この言語を知っていることで恩恵を受ける可能性が非常に高くなります。

機械を理解する(Cで考える)

コードの特定の部分の動作や他の言語の特定の機能について同僚と話し合うと、「Cで話す」ことになります。この部分はオブジェクトに「ポインター」を渡すのですか、それともオブジェクト全体をコピーするのですか。 ここで「キャスト」が発生する可能性はありますか? 等々。

高水準言語のコードの一部の動作を分析するときに、コードの一部が実行しているアセンブリ命令について議論(または考える)することはめったにありません。 代わりに、マシンが何をしているのかを議論するとき、私たちはCでかなりはっきりと話します(または考えます)。

さらに、自分がしていることについて立ち止まってそのように考えることができない場合、(魔法のように)物事がどのように行われるかについてのある種の迷信でプログラミングを終えることになるかもしれません。

Cのマシンのように考える

多くの興味深いCプロジェクトに取り組む

大きなデータベースサーバーやオペレーティングシステムカーネルから、個人的な満足と楽しみのために自宅で実行できる小さな組み込みアプリケーションまで、多くの興味深いプロジェクトがCで行われています。単一の理由で好きなことをやめる理由はありません。古くて小さいが、Cのような強力で実績のあるプログラミング言語を知らないこと。

Cでクールなプロジェクトに取り組む

結論

イルミナティは世界を動かしていません。 Cプログラマーはそうします。
つぶやき

Cプログラミング言語には有効期限がないようです。 ハードウェアに近く、移植性が高く、リソースの使用量が確定的であるため、オペレーティングシステムカーネルや組み込みソフトウェアなどの低レベルの開発に最適です。 その汎用性、効率性、および優れたパフォーマンスにより、データベースや3Dアニメーションなどの複雑性の高いデータ操作ソフトウェアに最適です。 今日の多くのプログラミング言語がCよりも意図された用途に優れているという事実は、それらがすべての分野でCに勝っていることを意味するわけではありません。 パフォーマンスが優先される場合でも、Cは卓越しています。

世界はC電源デバイスで実行されています。 私たちは、気付いているかどうかにかかわらず、これらのデバイスを毎日使用しています。 Cは過去、現在、そして私たちが見る限り、ソフトウェアの多くの分野の未来です。

関連: CおよびC ++言語を学ぶ方法:究極のリスト