たとえば、過去のSysInternalsツール「FileMon」には、ソースコードが完全に1つの4,000行のファイルにあるカーネルモードドライバーがあります。これは、これまでに作成された最初のpingプログラム(〜2,000 LOC)でも同じです。
たとえば、過去のSysInternalsツール「FileMon」には、ソースコードが完全に1つの4,000行のファイルにあるカーネルモードドライバーがあります。これは、これまでに作成された最初のpingプログラム(〜2,000 LOC)でも同じです。
回答:
複数のファイルを使用するには、常に追加の管理オーバーヘッドが必要です。コンパイルとリンクの段階を分けてビルドスクリプトやメイクファイルを設定し、異なるファイル間の依存関係が正しく管理されていることを確認し、電子メールまたはダウンロードでソースコードを簡単に配布できるように「zip」スクリプトを作成します。オン。今日の現代のIDEは通常、その負担の多くを取りますが、最初のpingプログラムが作成された時点では、そのようなIDEは利用できませんでした。また、〜4000 LOCの小さなファイルの場合、複数のファイルを適切に管理するIDEがなければ、前述のオーバーヘッドと複数のファイルを使用する利点のトレードオフにより、単一ファイルのアプローチを決定できる場合があります。
Cはモジュール化が苦手だからです。乱雑になり(ヘッダーファイルと#includes、extern関数、リンク時エラーなど)、持ち込むモジュールが増えるほど、複雑になります。
より現代的な言語は、Cの間違いから学んだため、モジュール化機能が優れており、コードベースをより小さくシンプルなユニットに簡単に分解できるためです。しかし、Cを使用すると、それ以外の場合は大量のコードと見なされるものを1つのファイルにまとめることを意味する場合でも、そのような問題をすべて回避または最小限に抑えることができます。
歴史的な理由とは別に、最新のパフォーマンス重視のソフトウェアでこれを使用する理由が1つあります。すべてのコードが1つのコンパイル単位にある場合、コンパイラはプログラム全体の最適化を実行できます。個別のコンパイル単位では、コンパイラは特定の方法(特定のコードのインライン化など)でプログラム全体を最適化できません。
リンカは、コンパイラができることだけでなく、確かにいくつかの最適化を実行できますが、すべてではありません。たとえば、最新のリンカは、複数のオブジェクトファイル間でさえ、参照されていない関数を除外するのに非常に優れています。他の最適化を実行できる場合もありますが、関数内でコンパイラーができることとは異なります。
単一ソースコードモジュールのよく知られた例の1つはSQLiteです。詳細については、SQLite Amalgamationページをご覧ください。
1。エグゼクティブサマリー
100を超える個別のソースファイルが連結され、「sqlite3.c」という名前の「統合」と呼ばれるCコードの単一の大きなファイルになります。統合には、アプリケーションがSQLiteを埋め込むために必要なすべてが含まれています。統合ファイルの長さは180,000行を超え、サイズは6メガバイトを超えます。
SQLiteのすべてのコードを1つの大きなファイルにまとめると、SQLiteの展開が簡単になります。追跡するファイルは1つだけです。また、すべてのコードが単一の翻訳単位にあるため、コンパイラーはプロシージャー間の最適化を改善して、マシンコードを5%〜10%高速化できます。
$(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(CFILES)
すべてを単一のsoudceファイルに移動するよりも、ビルドスクリプトを変更する方がはるかに簡単です。プロダクションターゲットのプロファイリングとデバッグをオフにする方法と同様に、変更されていないソースファイルの再コンパイルをスキップする従来のビルドスクリプトの代替ターゲットとして、プログラム全体のコンパイルを行うこともできます。すべてが1つの大きなヒープソースにある場合、そのオプションはありません。人々が慣れているものではありませんが、面倒なことは何もありません。
他の回答者が言及した単純さの要因に加えて、多くのCプログラムは1人の個人によって書かれています。
個人のチームがある場合、アプリケーションを複数のソースファイルに分割して、コード変更の不必要な競合を回避することが望ましくなります。特に、プロジェクトに取り組んでいる上級プログラマーと非常に若いプログラマーの両方がいる場合。
一人が一人で作業しているとき、それは問題ではありません。
個人的に、私は習慣的なものとして機能に基づいて複数のファイルを使用しています。しかし、それは私だけです。
C89にはinline
機能がなかったからです。つまり、ファイルを関数に分割すると、スタックに値をプッシュしてジャンプするオーバーヘッドが発生します。これにより、1つの大きなswitchステートメント(イベントループ)でコードを実装する場合に比べてかなりのオーバーヘッドが追加されました。しかし、イベントループは、モジュール化されたソリューションよりも、効率的に(または正確に)実装するのが常にはるかに困難です。そのため、大規模なプロジェクトの場合、人々はモジュール化を拒否します。しかし、設計を事前に考え、1つのswitchステートメントで状態を制御できる場合、彼らはそれを選択しました。
今日では、Cでも関数をインライン化できるため、モジュール化するためにパフォーマンスを犠牲にする必要はありません。
inline
、C89コンパイラにキーワードがなかったためインライン化できなかったため、1つの巨大な関数ですべてを記述しなければならなかったと主張するのは誤りです。inline
パフォーマンスの最適化として使用することはほとんどないはずです-コンパイラは一般にあなたよりもよく知っているでしょう(そしてキーワードを無視することもできます)。
inline
キーワードは、インライン最適化を実行するか否かの問題よりも重要であるリンカ関連のセマンティクスを持っていますが、いくつかの実装は、インライニングを制御すると、そのような事は時には非常に重要になることが他のディレクティブを持っています。場合によっては、関数は大きすぎてインライン化する価値がないように見えるかもしれませんが、定数の折りたたみはサイズと実行時間をほとんどゼロに減らすかもしれません。
これは進化の例としてカウントされますが、まだ言及されていないことに驚いています。
プログラミングの暗い日には、単一のFILEのコンパイルに数分かかることがありました。プログラムがモジュール化されている場合、必要なヘッダーファイルをインクルードすると(プリコンパイル済みヘッダーオプションはありません)、速度低下の大きな原因になります。さらに、コンパイラは、おそらく自動スワップファイルの利点なしに、ディスク自体に情報を保持することを選択/必要とする場合があります。
これらの環境要因が進行中の開発プラクティスに引き継がれる習慣は、徐々にゆっくりと順応してきました。
当時、単一のファイルを使用することで得られる利益は、HDDの代わりにSSDを使用することで得られる利益に似ています。