web-dev-qa-db-ja.com

あるCソースファイルを別のCソースファイルに含めますか?

別の.cファイルの#include .cファイルを使用しても問題ありませんか?プロジェクトファイルに含まれるとどうなりますか?

97
srujan

適切に使用すると、これは便利なテクニックになります。

かなり小さなパブリックインターフェイスと再利用不可能な実装コードを多く含む、複雑でパフォーマンスが重要なサブシステムがあるとします。このコードは、数千行、100個程度のプライベート関数、およびかなりの量のプライベートデータまで実行されます。自明でない組み込みシステムを使用している場合、おそらくこの状況に十分な頻度で対処できます。

ソリューションはおそらく階層化され、モジュール化され、分離されます。これらの側面は、サブシステムのさまざまな部分をさまざまなファイルにコーディングすることで、効果的に表現および強化できます。

Cを使用すると、これを行うことで多くを失う可能性があります。ほとんどすべてのツールチェーンは、単一のコンパイルユニットに対して適切な最適化を提供しますが、externと宣言されたものについては非常に悲観的です。

すべてを1つのCソースモジュールに入れると、次のようになります-

  • パフォーマンスとコードサイズの改善-関数呼び出しは多くの場合インライン化されます。インライン化しない場合でも、コンパイラーはより効率的なコードを生成する機会があります。

  • リンクレベルのデータと関数の非表示。

  • 名前空間の汚染とその結果の回避-扱いにくい名前を少なく使用できます。

  • コンパイルとリンケージの高速化。

しかし、このファイルを編集することになると、ひどい混乱を招き、暗黙のモジュール性を失います。これは、ソースを複数のファイルに分割し、それらを含めて単一のコンパイル単位を生成することで克服できます。

ただし、これを適切に管理するには、いくつかの規則を課す必要があります。これらはツールチェーンにある程度依存しますが、一般的なポインタは次のとおりです-

  • パブリックインターフェイスを別のヘッダーファイルに入れます-とにかくこれを行う必要があります。

  • すべての補助.cファイルを含む1つのメイン.cファイルがあります。これには、パブリックインターフェイスのコードも含まれます。

  • コンパイラガードを使用して、プライベートヘッダーとソースモジュールが外部コンパイルユニットに含まれないようにします。

  • すべてのプライベートデータと関数は静的に宣言する必要があります。

  • .cファイルと.hファイルの概念的な区別を維持します。これは、既存の規則を活用します。違いは、ヘッダーに多くの静的宣言があることです。

  • ツールチェーンが理由を課さない場合、プライベート実装ファイルに.cおよび.hという名前を付けます。インクルードガードを使用する場合、これらはコードを生成せず、新しい名前を導入しません(リンク中にいくつかの空のセグメントになる場合があります)。大きな利点は、他のツール(IDEなど)がこれらのファイルを適切に処理することです。

106

大丈夫ですか?はい、コンパイルされます

お勧めですか? no-.cファイルは.objファイルにコンパイルされ、コンパイル後に(リンカーによって)実行可能ファイル(またはライブラリ)にリンクされるため、1つの.cファイルを別のファイルに含める必要はありません。代わりにおそらくあなたがしたいことは、他の.cファイルで利用可能な関数/変数をリストする.hファイルを作成し、.hファイルを含めることです

49
Steven A. Lowe

番号。

ビルド環境に応じて(指定しない)、希望どおりに機能することがわかります。

ただし、*。cをコンパイルすることを期待する多くの環境(IDEと多くの手作りのMakefileの両方)があります。その場合、シンボルの重複によるリンカーエラーが発生する可能性があります。

原則として、この慣行は避けるべきです。

ソースを絶対に#includeする必要がある場合(通常は避ける必要があります)、ファイルに別のファイルサフィックスを使用します。

11

私のチームが.cファイルを含めることにした状況を共有すると思いました。私たちのアーキテクチャは主に、メッセージシステムを介して分離されたモジュールで構成されています。これらのメッセージハンドラーはパブリックであり、多くのローカルの静的ワーカー関数を呼び出して作業を行います。このプライベート実装コードを実行する唯一の方法は、パブリックメッセージインターフェイスを介して間接的に行うため、ユニットテストケースのカバレッジを取得しようとしたときに問題が発生しました。いくつかのワーカー関数がスタックの膝まで深くあるため、これは適切なカバレッジを達成するための悪夢であることが判明しました。

.cファイルを含めることで、テストで興味深いマシンの歯車に到達することができました。

9
Matthew Alford

ファイルの拡張子は、ほとんどのCコンパイラにとって重要ではないため、機能します。

ただし、メイクファイルまたはプロジェクトの設定によっては、含まれているcファイルが個別のオブジェクトファイルを生成する場合があります。リンクすると、シンボルが二重に定義される場合があります。

5
HS.

Linuxのgccコンパイラを使用して、1つの出力で2つのcファイルをリンクできます。 2つのcファイルがあり、一方が「main.c」で、もう一方が「support.c」であるとします。したがって、これら2つをリンクするコマンドは

gcc main.c support.c -o main.out

これにより、2つのファイルが単一の出力main.outにリンクされます。出力を実行するには、コマンドは次のようになります。

./main.out

Support.cファイルで宣言されているmain.cの関数を使用している場合、externストレージクラスを使用してmainで宣言する必要があります。

3
sumitroy

.Cまたは.CPPファイルを他のソースファイルに適切に含めることができます。 IDEに応じて、通常、含めるソースファイルプロパティを右クリックしてプロパティをクリックし、コンパイル/リンク/ビルドから除外するなどのオプションをオフにすることで、二重リンクを防止できます。多分。または、プロジェクト自体にファイルを含めることができなかったため、IDEはファイルの存在すら知らず、コンパイルを試行しません。また、メイクファイルを使用すると、コンパイルやリンクのためにファイルを入れないだけです。

編集:申し訳ありませんが、他の回答への返信ではなく回答にしました:(

2
MikeyPro

C言語はそのような#includeを禁止していませんが、結果の翻訳単位は依然として有効なCでなければなりません。

.prjファイルで使用しているプログラムがわかりません。 「make」やVisual Studioなどを使用している場合は、独立してコンパイルできないファイルを使用せずに、コンパイルするファイルのリストを必ず設定してください。

1

Cファイルを別のファイルに含めることは合法ですが、なぜこれを行っているのか、何を達成しようとしているのかを正確に把握していない限り、行うことはお勧めできません。
あなたの質問の背後にあるあなたの目標を達成するためのより適切な方法をコミュニティが見つける理由をここに投稿するとほぼ確信しています(「ほぼ」に注意してください。コンテキストが与えられたソリューション)。

ところで、質問の2番目の部分を逃しました。 Cファイルが別のファイルに含まれ、同時にプロジェクトに含まれる場合、オブジェクトをリンクする理由が重複シンボルの問題になる可能性があります。つまり、同じ関数が2回定義されます(すべて静的でない限り)。

0
Ilya