2017-07-26 3 views
1

Ich schreibe ein Programm in C und teile die Teile in verschiedene .c-Dateien (Module).C - den Code korrekt in mehrere Dateien partitionieren

Einige Module (sagen B und C) enthalten das gleiche Modul (sagen A) und wenn diese Module (B und C) von einem anderen Modul enthalten sind (zB D), entsteht eine Rautenhierarchie, die Neudefinitionsprobleme verursacht .

Eg.

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

#include <stdio.h> 
#include <float.h> 
#include "sphere.c" 
#include "camera.c" 

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

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

In diesem Fall ergibt sich das Problem, weil sowohl camera.c und sphere.c ray.c gehören (und daher auch vec3.c), so dass die in diesen Modulen definierten Strukturen und Funktionen doppelt definiert sind.

Wie kann ich den Code neu anordnen, um diese Probleme nicht zu haben?

Danke!

+2

Sie sollten ** niemals ** Quelldateien einschließen. Schreiben Sie Header-Dateien ('.h') mit ** nur ** Deklarationen und * include-guard-Makros *. –

+1

Es gibt keine einzige Regel, um ein Projekt in mehrere Dateien aufzuteilen. * Außer *, dass Strukturen und Deklarationen (wie Funktionsprototypen) in * Header-Dateien * sein sollten und Sie niemals Quelldateien "einschließen" sollten! –

Antwort

1

Das ist keine Sprache definierte Regel über Organisation Datei . Tatsächlich bilden alle Dateien, die in Ihrer Quelldatei enthalten sind, eine sogenannte "Kompilierungseinheit", die von einem Precompiler erstellt wird, und werden vom Compiler als einzelne Datei behandelt. Die Precompiler-Direktive #include tut dies wörtlich ... schließt diese andere Datei in die aktuelle Datei ein, die gerade verarbeitet wird. Daher ist es wichtig sicherzustellen, dass Dateien nur einmal enthalten sind.

Eine klassische Methode ist die Verwendung der #ifndef-Direktive des Präprozessors. Die meisten Compiler unterstützen die Direktive #pragma once, aber es ist kein Standart-Tool, das sich manchmal im Fall von zirkulären Includes unvorhersehbar verhält.

Es ist gängige Praxis, Dateien aufzurufen, die wiederverwendbare Deklarationen und Definitionen "headers" enthalten und ihnen .h (und manchmal .hpp) geben. C++ - Standard-Header stimmten überein, keine Erweiterung zu haben, um zu vermeiden, sie mit C-Headern zu mischen. Z.B. stdio.h wird von cstdio ersetzt und exclusiv sollte nicht in C++ verwendet werden. Manchmal ist es erforderlich, große Repetitive-Definitionen zu verwenden, die unterschiedliche Endungen wie .inc, .incl, etc. haben können.

Warum also Leute sagen "niemals .c-Dateien einschließen". Es bezieht sich auf die verwendete Toolchain, die Dienstprogramme, die den Erstellungsprozess von Anwendungen steuern. Sie würden den Compiler fast nie manuell ausführen. Es gibt immer ein Bauwerkzeug, das normalerweise entscheidet, was mit der Datei zu tun ist, basierend auf der Erweiterung. .c-Dateien werden normalerweise als separate Kompilierungsmodule betrachtet, daher würde das Tool den Compiler für JEDEN von ihnen ausführen, bevor versucht wird, sie miteinander zu verknüpfen.

Header sollten keine Objekte wie Funktionen oder Variablen definieren, sie können sie für die externe Verknüpfung deklarieren. Warum? Wenn Sie eine Headerdatei mit definierter Variable in mehrere Kompilierungseinheiten einfügen, erhalten Sie einen Fehler vom Linker, da alle Einheiten dasselbe Symbol enthalten. Also sollte die Definition einzigartig sein oder das Programm als schlecht geformt betrachtet werden. Für Funktionen wird standardmäßig eine externe Verknüpfung vorausgesetzt

/* myheader.h */ 
#ifndef ___MYHEADER_H 
#define ___MYHEADER_H 

extern int globalVariable; 

typedef struct MyType { 

}; 

int foo(MyType); 

#endif 

/* myheader.cpp */ 
#include "myheader.h" 

int globalVariable = 5; 

int foo(MyType param) 
{ 
/* body of foo */ 
} 

Wie die Dateien im Projekt organisiert sind, hängt vom Designer ab. Wenn Sie Teil eines Teams sind, wird von Ihnen erwartet, dass Sie den Empfehlungen des Lead-Designers oder der genehmigten Dokumentation des Teams folgen.

2

Sie sollten niemals .c Dateien einschließen.

Erstellen Sie für jede Implementierungsdatei (.c) eine Headerdatei (.h) und deklarieren Sie darin die Funktionen. Fügen Sie dann die Header-Datei stattdessen in die entsprechende Implementierungsdatei ein. Sobald Sie deklariert und eingebunden sind, sollten Sie die Implementierung dieser deklarierten Funktionen in Ihre Implementierungsdatei schreiben.

Dann, um die Funktionen verwenden Sie die Header-Datei enthalten.

Beispiel für eine Header-Datei (example.h):

#ifndef EXAMPLE_H 
#define EXAMPLE_H 

/* Function declarations */ 
void example(); 

#endif /* EXAMPLE_H */ 

Beispiel einer Implementierungsdatei (example.c):

#include "example.h" 

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

Danke für den Hinweis! Das habe ich versucht, aber das Problem bleibt auf den Strukturen und typedef: sie müssen in der .c-Datei oder in der .h platziert werden? Zum Beispiel, für das vec3.c-Modul, wo wird die Definition der vec3-Struktur platziert? Ich danke dir sehr! –

+0

@ArcticPi Für Funktionen, auf die andere .c-Dateien zugreifen müssen, sollten sie in der .h-Datei deklariert werden. Schließen Sie dann die .h-Datei in die .c-Datei ein, die Ihre Funktion verwenden soll. – Houssni

+0

@Arctic Pi diese Deklaration, wenn sie wiederverwendbar sein sollen, legen Sie sie in den Header. Während typedef die Typdefinition bedeutet, heißt die syntaktische Einheit, die mit dem Schlüsselwort beginnt, typedef-Deklaration. Siehe meine Antwort – Swift

Verwandte Themen