C Fragen zu Header Dateien

R3ddy

Cadet 4th Year
Registriert
Juli 2010
Beiträge
79
Ich beschäftige mich jetzt seit einiger Zeit mit C99 (hauptsächlich für numerische Berechnungen). Bislang habe ich immer nur kleinere Sachen programmiert, bei denen eine Aufteilung des Programmcodes in mehrere Dateien nicht notwendig war. Nun bin ich aber an einem Punkt angekommen, an dem es mir sinnvoll erscheint Teile des Codes zwecks der Übersicht auszulagern.

Bisher konnte ich in Erfahrung bringen, dass man zu einer Quellcodedatei "*.c" eine Headerdatei "*.h" erzeugt, in der man die Funktionen und Variablen deklariert, die in der Quellcodedatei definiert werden. Die Headerdatei wird dann mittels "#include" sowohl in die zugehörige Quellcodedatei, als auch in den Programmteilen, in die Funktionalität des ausgelagerten Quellcodes benötigt wird eingebunden.
Das Ganze dient wohl dazu, dass der Compiler immer nur die veränderten Quellcodedateien zu übersetzen braucht.

Nach dem Vorgeplänkel jetzt zu meinen eigentlichen Fragen:
  1. Stimmt das oben Geschriebene?
  2. Kommen die Deklarationen von Funktionen bzw. Variablen, die ausschließlich in der zugehörigen Quellcodedatei benötigt werden auch in die Headerdatei und sollte in diesem Fall das Schlüsselwort static verwendet werden?
  3. Sollte man zu der Hauptdatei (in der main definiert ist) auch eine Headerdatei anlegen?
Das wär es dann auch erstmal (vielleicht fallen mir aber Später noch weitere Fragen ein). Würde mich sehr über Antworten freuen. Am besten wäre es, wenn ihr auch begründen würdet, warum es so gemacht wird und nicht anders.
 
Headerdateien sind gewöhnliche C++-Dateien, die im Normalfall Funktionsdeklarationen und ähnliches enthalten. Sicher werden Sie sich erinnern: Deklarationen machen dem Compiler bekannt, wie etwas benutzt wird. Wenn Sie also eine Headerdatei einbinden und darin eine Funktionsdeklaration steht, dann weiß der Compiler wie die Funktion aufgerufen wird. Der Compiler weiß zu diesem Zeitpunkt nicht, was die Funktion tut, aber das ist auch nicht nötig um sie aufrufen zu können.
Das Einbinden einer Headerdatei erfolgt über die Präprozessordirektive #include. Der Code in der Datei die mit include referenziert wird, wird vom Präprozessor einfach an der Stelle eingefügt, an der das include stand.
Was genau meinst du mit veränderten Quellcodedateien übersetzen? Normalerweise inkludierst du die Headerfiles in den Teil deines Quellcodes an dem du die benötigten Funktionen benutzen willst.
 
Ich habe es so verstanden, dass wenn nur eine "*.c" Datei verändert wird, nur diese erneut vom Compiler übersetzt werden muss. Der Linker fügt dann schließlich die bereits übersetzen, nicht veränderten Dateien mit der neu übersetzen Datei zusammen.

Ist das so nicht korrekt?
 
Ich würde sagen grundsätzlich ja.

Der Compiler sollte auch in der Lage sein durch die definierten Abhängigkeiten,
die Du durch das #include schaffst, auch nach Änderungen in einem Header zu erkennen, welche c-Dateien dadurch neu kompiliert werden müssen.
Ich selbst habe aber schon die Erfahrung gemacht, daß bei manchen Compilern das nicht ganz so sauber funktioiniert,
und bei Header-Änderungen ein kompletter Rebuild notwendig sein kann.


Zu den gestellten Fragen wären meine Antworten:

1) Grundsätzlich ja.
Aber es sollten in einer Headerdatei nur Funktionsdeklarationen/Makros/Konstanten stehen,
die auch in anderen Modulen (c-Dateien) bekannt sein sollen.
Variablendeklarationen hier vermeiden wenn möglich, die sind sonst in allen Modulen als gleichnamige
"Kopien" vorhanden.
Das kann unangenehme Compilerfehler und Nebeneffekte bewirken.

Auf Variablen in anderen Modulen besser über entsprechende Set-/Get-Funktionen zugreifen
(oder über extern-Deklaration im anderen Modul, wenn es sein muss).
Dann ist z.B. die Berechnung/Aktualisierung und die Verwendung der Variablen sauber getrennt.


2) Besser nicht.
Theoretisch kann ja jederzeit "jemand" kommen und die Headerdatei trotzdem in anderen Modulen einbinden.
Was nur in einem Modul verwendet wird gehört auch nur dort hinein.
Mit "static" damit es nicht "versehentlich" woanders referenziert wird.

Es gibt natürlich die Möglichkeit im einem Projekt 2 Arten von Headerdateien zu benutzen.
a) Modulname.h -> Hier alles rein was nur zum Aufräumen/Übersichtlichkeit aus Modulname.c verlagert wird.
b) Modulname_if.h -> if = Interface, Hier alles rein was nach außen bekannt sein soll.
Und dann nur b) in anderen Modulen einbinden.


3) Kommt darauf an was Du im Code benötigst.
Es gibt keinen "generellen Zwang" für jede c-Datei auch eine Headerdatei zu haben.


Es empfiehlt sich auch den gesamten Code-Inhalt in der Headerdatei so zu klammern:

#ifndef HEADERNAME
#define HEADERNAME
...
Code
...
#endif

Das verhindert das sich durch das Inkludieren von "zentralen" Headern wie z.B. für Datentypdefinitionen
in mehreren Modulen sind, dann u.U. Compilerprobleme (multiple definition) ergeben,
wenn dann deren Header in weiteren Modulen inkludiert werden.
 
Vielen Dank für deine ausführliche Antwort. Das hat mir sehr geholfen.

Allerdings hätte ich jetzt noch zwei weitere Fragen:
  1. Wenn ich in "modulname.c" die Funktionalität von z.B. "math.h" benötige, schreibt man das "#include <math.h>" dann besser in "modulname.c" oder doch in "modulname.h" ("modulname.h" soll dabei der Header sein, der auch in anderen Dateien eingebunden wird)?
  2. Gibt es eine Konvention, ob die Dateinamen mit einem großen oder kleinen Buchstaben beginnen sollen? (Die Standard Header sind ja alle kleingeschrieben, W1010 hat die Dateinamen allerdings groß geschrieben.)
 
@W1010
der compiler entscheidet nicht was neu compiliert werden soll (z.m. nicht im falle der gängigen c/c++ compiler wie gcc etc.).
der compileirt immer alles was man ihm vorwirft.

das build-management system kümmert sich um sowas, z.b. make oder be MS Visual Studio c++ eben das.
 
Zu Frage 2 des Threaderstellers:

In einen Header nicht exportierte Funktionen als static zu deklarieren ist eine Fehlerquelle.
Wenn man die entsprechende Funktion aus Unachtsamkeit aufruft, wird der Compiler ganz
vergnügt einen Aufruf erzeugen, der aber später vom Linker nicht aufgelöst werden kann.

Dann wundert man sich unter Umständen, was man falsch gemacht hat. Auch ist es nicht
sinnvoll, Funktionen, die man gar nicht aufrufen kann, zu deklarieren. Wundert mich, dass
da überhaupt jemand auf diese Idee kommt.
 
Zurück
Oben