2017-07-26 11 views
1

私はCでプログラムを作成していますが、別の.cファイル(モジュール)に分割しています。C - コードを複数のファイルに適切に分割する

モジュールの中には同じモジュール(たとえばA)が含まれており、これらのモジュール(BとC)が別のモジュール(Dなど)に含まれていると、再定義の問題を引き起こすダイヤモンド " 。

例:

vec3.c

#include <math.h> 

typedef struct { 
    float x, y, z; 
} vec3; 

float dot(vec3 v1, vec3 v2) { 
    return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; 
} 

/* ... other stuff ... */ 

ray.c

#include "vec3.c" 

typedef struct { 
    vec3 A, B; 
} ray; 

vec3 origin(ray r) { 
    return r.A; 
} 

/* ... other stuff ... */ 

sphere.c

#include "ray.c" 

typedef struct { 
    float t; 
    vec3 p; 
    vec3 n; 
} record; 

typedef struct { 
    vec3 center; 
    float radius; 
} sphere; 

typedef enum {false, true} bool; 

bool hit_sphere(sphere s, ray r, float tmin, float tmax, record *rec) { 
    /* ... do stuff ... */ 
} 

/* ... other stuff ... */ 

camera.c

#include "ray.c" 

typedef struct { 
    vec3 origin; 
    vec3 lower_left_corner; 
    vec3 horizontal; 
    vec3 vertical; 
} camera; 

/* ... other stuff ... */ 

vec3 origin = {0.0, 0.0, 0.0}; 
vec3 lower_left_corner = {-2.0, -1.0, -1.0}; 
vec3 horizontal = {4.0, 0.0, 0.0}; 
vec3 vertical = {0.0, 2.0, 0.0}; 

camera cam = {origin, lower_left_corner, horizontal, vertical}; 

main.cの

camera.cとsphere.c両方もvec3.cしたがってray.cを含む(そしてため、問題が生じる。この場合
#include <stdio.h> 
#include <float.h> 
#include "sphere.c" 
#include "camera.c" 

/* ... other stuff ... */ 

int main() { 
    /* ... do stuff ... */ 
} 

)ので、これらのモジュールで定義されている構造と関数の二重定義があります。

これらの問題が発生しないようにコードを並べ替えるにはどうすればよいですか?

ありがとうございます!

+2

ソースファイルを '#include'することは絶対にしないでください。 ** only **宣言と* include-guardマクロ*を含むヘッダファイル( '.h')を記述してください。 –

+1

プロジェクトを複数のファイルに分割するルールは1つではありません。 *構造体や宣言(関数プロトタイプのようなもの)は*ヘッダファイル*にあるべきであることを除いて、あなたはソースファイルを '#インクルードするべきではありません! –

答えて

1

ザは、ファイル構成についての言語に定義されたルールはありません。実際、ソース・ファイルに含まれるすべてのファイルは、プリコンパイラによって作成されたいわゆる「コンパイル・ユニット」を形成し、コンパイラによって1つのファイルとして扱われます。プリコンパイラの指示文#includeは、実際にはその他のファイルを処理中の現在のファイルに含めます。したがって、ファイルが1回だけ含まれるようにすることが不可欠です。

これを行う典型的な方法は、プリプロセッサの#ifndef指示文を使用することです。ほとんどのコンパイラは指令#pragma onceをサポートしていますが、これはスタンドアーツツールではなく、循環インクルードの場合に予期せず動作することがあります。

再利用可能な宣言と定義 "ヘッダー"を含むファイルを呼び出し、それらに.h(と.hpp)を与えることが慣習として受け入れられています。 C++標準ヘッダーは、Cヘッダーとの混同を避けるために拡張子を全く持たないことに同意しました。例えば。 stdio.hはcstdioに置き換えられ、前者はC++で使用すべきではありません。場合によっては、大きな繰り返し定義を含む要求があり、異なる拡張子.inc、.inclなどを得ることができます。

なぜ、人々は ".cファイルを決して含めません"と言うのですか?これは、アプリケーションの構築プロセスを制御する一連のユーティリティであるツールチェーンに関連しています。コンパイラを手動で実行することはほとんどありません。通常は、ビルド・ツールがあります。ビルド・ツールは、通常、ファイルに関する処理を決定し、その決定に基づいて拡張を行います。 .cファイルは通常、別々のコンパイルモジュールを表すと見なされるため、ツールはそれらをリンクさせる前に、それぞれのコンパイラを実行します。

ヘッダーは関数や変数などのオブジェクトを定義すべきではなく、外部リンク用に宣言することができます。どうして?定義された変数を持つヘッダファイルをいくつかのコンパイル単位に含めると、すべてのユニットに同じシンボルが含まれるため、リンカからエラーが発生します。したがって、定義は一意でなければならないか、プログラムは不正な形であるとみなされます。デフォルトで外部リンクが常にあると仮定される関数

プロジェクトでファイルがどのように整理されるかは、デザイナーの責任です。あなたがチームの一員である場合、そのチームのリードデザイナーの推奨事項または承認されたドキュメントに従うことが期待されます。

2

ファイルには.cファイルを含めないでください。

各実装ファイル(.c)、ヘッダファイル(.h)を作成し、その中に関数を宣言します。次に、対応する実装ファイルにヘッダファイルをインクルードします。一度宣言して組み込むと、宣言された関数の実装を実装ファイルに記述する必要があります。

次に、ヘッダーファイルを含める関数を使用します。

ヘッダファイル(example.h)の例:

#ifndef EXAMPLE_H 
#define EXAMPLE_H 

/* Function declarations */ 
void example(); 

#endif /* EXAMPLE_H */ 

実装ファイルの例(はexample.c):

#include "example.h" 

void example() { 
    /* TODO: add logic */ 
} 
+0

アドバイスをいただきありがとうございます!それは私がやろうとしたことですが、問題は構造体とtypedefに残ります。それらは.cファイルまたは.hに入れなければなりません。たとえば、vec3.cモジュールの場合、vec3構造の定義はどこに置かれますか?どうもありがとうございます! –

+0

@ArcticPi他の.cファイルからアクセス可能でなければならない関数の場合、.hファイルで宣言する必要があります。次に、あなたの関数を使用する.cファイルの.hファイルを#includeします。 – Houssni

+0

@Arctic Piこれらは宣言であり、再利用可能であればヘッダに配置します。 typedefは型定義を意味しますが、キーワードで始まるシンタクチックユニットはtypedef宣言と呼ばれます。私の答えを見る – Swift

関連する問題