wake-up-neo.net

Soll ich #include in Headern verwenden?

Ist es notwendig, #include eine Datei, wenn in einem Header (* .h) die in dieser Datei definierten Typen verwendet werden?

Wenn ich beispielsweise GLib verwende und den Basistyp gchar in einer in meinem Header definierten Struktur verwenden möchte, muss ein #include <glib.h>, wissend, dass ich es bereits in meiner * .c-Datei habe?

Wenn ja, muss ich es auch zwischen #ifndef und #define oder nach dem #define?

68
Victor

Die NASA-Regeln Goddard Space Flight Center ( GSFC ) für Header in C besagen, dass es möglich sein muss, einen Header als einzigen Header in eine Quelldatei aufzunehmen, und Dieser Code wird dann unter Verwendung der in diesem Header bereitgestellten Funktionen kompiliert.

Dies bedeutet, dass der Header eigenständig, idempotent und minimal sein muss:

  • in sich geschlossen - Alle erforderlichen Typen werden definiert, indem bei Bedarf relevante Überschriften eingeschlossen werden.
  • idempotent - Zusammenstellungen machen keinen Fehler, selbst wenn sie mehrmals enthalten sind.
  • minimal - definiert nichts, was von Code, der den Header verwendet, um auf die durch den Header definierten Funktionen zuzugreifen, nicht benötigt wird.

Der Vorteil dieser Regel ist, dass jemand, der den Header verwenden muss, keine Probleme damit hat, herauszufinden, welche anderen Header ebenfalls enthalten sein müssen - er weiß, dass der Header alles Nötige enthält.

Der mögliche Nachteil ist, dass einige Header möglicherweise mehrmals enthalten sind. Aus diesem Grund sind die Header-Guards für Mehrfacheinschlüsse von entscheidender Bedeutung (und warum Compiler versuchen, das erneute Einschließen von Headern möglichst zu vermeiden).

Implementierung

Diese Regel besagt, dass, wenn der Header einen Typ verwendet, z. B. "FILE *" Oder "size_t", Sichergestellt werden muss, dass der entsprechende andere Header (<stdio.h> Oder <stddef.h> Sollte zum Beispiel enthalten sein. Eine häufig vergessene Konsequenz ist, dass der Header keinen anderen Header enthalten sollte, der nicht vom Benutzer des Pakets benötigt wird, um das Paket zu verwenden. Der Header sollte also minimal sein.

Darüber hinaus bieten die GSFC-Regeln eine einfache Technik, um sicherzustellen, dass dies der Fall ist:

  • In der Quelldatei, die die Funktionalität definiert, muss der Header der erste aufgelistete Header sein.

Nehmen wir also an, wir haben eine magische Sortierung.

magicsort.h

#ifndef MAGICSORT_H_INCLUDED
#define MAGICSORT_H_INCLUDED

#include <stddef.h>

typedef int (*Comparator)(const void *, const void *);
extern void magicsort(void *array, size_t number, size_t size, Comparator cmp);

#endif /* MAGICSORT_H_INCLUDED */

magicsort.c

#include <magicsort.h>

void magicsort(void *array, size_t number, size_t size, Comparator cmp)
{
    ...body of sort...
}

Beachten Sie, dass der Header einen Standardheader enthalten muss, der size_t Definiert. Der kleinste Standardheader, der dies tut, ist <stddef.h>, obwohl dies auch von mehreren anderen getan wird (<stdio.h>, <stdlib.h>, <string.h>, möglicherweise von einigen anderen).

Wie bereits erwähnt, ist es auch normal, dass zusätzliche Header für die Implementierungsdatei erforderlich sind, wenn sie andere Header benötigt. Die Implementierungsdatei ('magicsort.c') sollte sie jedoch selbst einschließen und sich nicht auf den Header stützen, um sie einzuschließen. Der Header sollte nur das enthalten, was Benutzer der Software benötigen. Nicht das, was die Implementierer brauchen.

Konfigurations-Header

Wenn Ihr Code einen Konfigurationsheader verwendet (z. B. GNU Autoconf und die generierte 'config.h'), müssen Sie diesen möglicherweise in 'magicsort.c' verwenden:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include "magicsort.h"

...

Dies ist das einzige Mal, dass mir bekannt ist, dass der private Header des Moduls nicht der allererste Header in der Implementierungsdatei ist. Die bedingte Aufnahme von 'config.h' sollte sich jedoch wahrscheinlich in 'magicsort.h' selbst befinden.


Update 2011-05-01

Die oben verlinkte URL ist nicht mehr funktionsfähig (404). Sie finden den C++ - Standard (582-2003-004) unter EverySpec.com ; Der C-Standard (582-2000-005) scheint in Aktion zu fehlen.

Die Richtlinien aus dem C-Standard waren:

§2.1 EINHEITEN

(1) Code ist als Einheit oder als eigenständige Header-Datei zu strukturieren.

(2) Eine Einheit besteht aus einer einzelnen Header-Datei (.h) und einer oder mehreren Body-Dateien (.c). Zusammen werden die Header- und Body-Dateien als Quelldateien bezeichnet.

(3) Eine Unit-Header-Datei enthält alle relevanten Informationen, die eine Client-Unit benötigt. Der Client einer Einheit muss nur auf die Header-Datei zugreifen, um die Einheit zu verwenden.

(4) Die Unit-Header-Datei muss # include-Anweisungen für alle anderen Header enthalten, die für den Unit-Header erforderlich sind. Auf diese Weise können Clients eine Unit verwenden, indem sie eine einzelne Header-Datei einschließen.

(5) Die Unit-Body-Datei muss vor allen anderen # include-Anweisungen eine # include-Anweisung für den Unit-Header enthalten. Auf diese Weise kann der Compiler überprüfen, ob sich alle erforderlichen # include-Anweisungen in der Headerdatei befinden.

(6) Eine Körperdatei darf nur Funktionen enthalten, die einer Einheit zugeordnet sind. Eine Body-Datei enthält möglicherweise keine Implementierungen für Funktionen, die in verschiedenen Headern deklariert sind.

(7) Alle Client-Einheiten, die einen Teil einer bestimmten Einheit U verwenden, müssen die Header-Datei für die Einheit U enthalten. Dies stellt sicher, dass es nur einen Ort gibt, an dem die Entitäten in der Einheit U definiert sind. Client-Einheiten dürfen nur die im Einheitenheader definierten Funktionen aufrufen. Sie dürfen keine Funktionen aufrufen, die im Body definiert, aber nicht im Header deklariert sind. Client-Einheiten dürfen nicht auf Variablen zugreifen, die im Hauptteil, aber nicht im Header deklariert sind.

Eine Komponente enthält eine oder mehrere Einheiten. Eine Mathematikbibliothek ist beispielsweise eine Komponente, die mehrere Einheiten wie Vektor, Matrix und Quaternion enthält.

Eigenständige Header-Dateien haben keine verknüpften Körper. Beispielsweise deklariert ein Header für allgemeine Typen keine Funktionen, sodass er keinen Textkörper benötigt.

Einige Gründe für das Vorhandensein mehrerer Textdateien für eine Einheit:

  • Ein Teil des Body-Codes hängt von der Hardware oder dem Betriebssystem ab, der Rest ist jedoch üblich.
  • Die Dateien sind zu groß.
  • Das Gerät ist ein allgemeines Dienstprogrammpaket, und einige Projekte verwenden nur einige der Funktionen. Durch das Einfügen jeder Funktion in eine separate Datei kann der Linker diejenigen, die nicht verwendet werden, aus dem endgültigen Bild ausschließen.

§2.1.1 Header enthalten Begründung

Dieser Standard verlangt, dass der Header einer Unit #include - Anweisungen für alle anderen Header enthält, die für den Unit-Header erforderlich sind. Wenn Sie #include Für den Unit-Header als Erstes in den Unit-Body einfügen, kann der Compiler überprüfen, ob der Header alle erforderlichen #include - Anweisungen enthält.

Ein alternatives Design, das von dieser Norm nicht zugelassen wird, erlaubt keine #include - Anweisungen in Kopfzeilen. Alle #include werden in den Body-Dateien ausgeführt. Unit-Header-Dateien müssen dann Anweisungen #ifdef Enthalten, mit denen überprüft wird, ob die erforderlichen Header in der richtigen Reihenfolge enthalten sind.

Ein Vorteil des alternativen Designs besteht darin, dass die Liste #include In der Textdatei genau die Abhängigkeitsliste ist, die in einem Makefile benötigt wird, und diese Liste vom Compiler überprüft wird. In der Standardausführung muss ein Tool zum Generieren der Abhängigkeitsliste verwendet werden. Alle von der Branche empfohlenen Entwicklungsumgebungen bieten jedoch ein solches Tool.

Ein Hauptnachteil des alternativen Designs besteht darin, dass, wenn sich die erforderliche Header-Liste einer Einheit ändert, jede Datei, die diese Einheit verwendet, bearbeitet werden muss, um die Anweisungsliste #include Zu aktualisieren. Außerdem kann die erforderliche Header-Liste für eine Compiler-Bibliothekseinheit auf verschiedenen Zielen unterschiedlich sein.

Ein weiterer Nachteil des alternativen Designs besteht darin, dass die Header-Dateien der Compiler-Bibliothek und andere Dateien von Drittanbietern geändert werden müssen, um die erforderlichen #ifdef - Anweisungen hinzuzufügen.

Eine andere gängige Praxis besteht darin, alle Systemheaderdateien vor allen Projektheaderdateien in Textdateien einzuschließen. Dieser Standard folgt nicht dieser Vorgehensweise, da einige Projektheaderdateien möglicherweise von Systemheaderdateien abhängen, weil sie entweder die Definitionen im Systemheader verwenden oder eine Systemdefinition überschreiben möchten. Solche Projekt-Header-Dateien sollten #include - Anweisungen für die System-Header enthalten. Wenn der Body sie zuerst einschließt, prüft der Compiler dies nicht.

Der GSFC-Standard ist über das Internetarchiv 2012-12-10 verfügbar

Information Mit freundlicher Genehmigung Eric S. Bullington :

Der referenzierte NASA C-Codierungsstandard kann über das Internetarchiv abgerufen und heruntergeladen werden:

http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard

Sequenzierung

Die Frage lautet auch:

Wenn ja, muss ich es auch zwischen #include Und #ifndef Oder nach dem #define Einfügen (die #define - Zeilen).

Die Antwort zeigt den richtigen Mechanismus - die verschachtelten Includes usw. sollten nach dem #define Stehen (und das #define Sollte die zweite nichtkommentierte Zeile in der Kopfzeile sein) - aber das tut es nicht erkläre, warum das richtig ist.

Überlegen Sie, was passiert, wenn Sie #include Zwischen #ifndef Und #define Setzen. Angenommen, der andere Header selbst enthält verschiedene Header, möglicherweise sogar indirekt #include "magicsort.h". Wenn die zweite Einbeziehung von magicsort.h Vor #define MAGICSORT_H_INCLUDED Erfolgt, wird der Header ein zweites Mal eingeschlossen, bevor die von ihm definierten Typen definiert werden. In C89 und C99 wird also jeder typedef -Typname fälschlicherweise neu definiert (in C2011 können sie auf denselben Typ umdefiniert werden). und Sie erhalten den Verarbeitungsaufwand die Datei mehrmals, was in erster Linie den Zweck des Header-Schutzes zunichte macht. Dies ist auch der Grund, warum #define Die zweite Zeile ist und nicht direkt vor #endif Geschrieben wird. Die angegebene Formel ist zuverlässig:

#ifndef HEADERGUARDMACRO
#define HEADERGUARDMACRO

...original content of header — other #include lines, etc...

#endif /* HEADERGUARDMACRO */
95

Es empfiehlt sich, #includes nur dann in eine Include-Datei einzufügen, wenn die Include-Datei dies benötigt. Wenn die Definitionen in einer bestimmten Include-Datei nur in der C-Datei verwendet werden, nehmen Sie sie nur in die C-Datei auf.

In Ihrem Fall würde ich es in die Include-Datei zwischen dem # ifdef/# endif aufnehmen.

Dies minimiert Abhängigkeiten, sodass Dateien, die kein bestimmtes Include benötigen, nicht neu kompiliert werden müssen, wenn sich die Include-Datei ändert.

20

Ich benutze das folgende Konstrukt, um sicherzugehen, dass die benötigte Include-Datei vor diesem Include enthalten ist. Ich beziehe alle Header-Dateien nur in Quelldateien ein.

#ifndef INCLUDE_FILE_H
 #error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h"
#endif
0
Thomas Weber

Während der Kompilierung ersetzt der Präprozessor nur die Anweisung #include durch den angegebenen Dateiinhalt. Um Endlosschleifen zu vermeiden, sollte es verwendet werden

#ifndef SOMEIDENTIFIER
#define SOMEIDENTIFIER
....header file body........
#endif

Wenn ein Header in einem anderen Header enthalten war, der in Ihre Datei aufgenommen wurde, muss er nicht erneut explizit aufgenommen werden, da er rekursiv in die Datei aufgenommen wird

0
Alex

Ja, es ist notwendig, oder der Compiler wird sich beschweren, wenn er versucht, Code zu kompilieren, der ihm nicht "bewusst" ist. Denken Sie daran, dass # include's ein Hinweis/Nudge/Elbow für den Compiler sind, um ihm mitzuteilen, dass er die Deklarationen, Strukturen usw. abholen soll, damit das Kompilieren erfolgreich ist. Der # ifdef/# endif-Header-Trick, auf den jldupont hinweist, besteht darin, die Kompilierung von Code zu beschleunigen.

Es wird in Fällen verwendet, in denen Sie einen C++ - Compiler haben und einfachen C-Code wie hier gezeigt kompilieren Hier ist ein Beispiel für den Trick:

 # ifndef __MY_HEADER_H __ 
 # define __MY_HEADER_H __ 
 
 # ifdef __cplusplus 
 extern "C" {
 # endif 
 
 
/* C-Code wie Strukturen, Deklarationen usw. */
 
 # Ifdef __cplusplus 
} 
 # endif 
 
 # endif/* __MY_HEADER_H__ */

Wenn dies mehrmals enthalten war, wird der Compiler es nur einmal enthalten, da das Symbol __MY_HEADER_H__ ist einmal definiert, was die Übersetzungszeiten verkürzt. Beachten Sie das Symbol cplusplus im obigen Beispiel, das die normale Standardmethode zum Kompilieren von C++ ist, wenn ein C-Code herumliegt.

Ich habe das Obige beigefügt, um dies zu zeigen (obwohl es für die ursprüngliche Frage des Posters nicht wirklich relevant ist). Hoffe das hilft, Viele Grüße, Tom.

PS: Es tut mir leid, dass ich dies abgelehnt habe, da ich dachte, es wäre ein nützlicher Leckerbissen für C/C++-Neulinge. Hinterlasse einen Kommentar/Kritik usw., da sie sehr willkommen sind.

0
t0mm13b

Sie müssen den Header aus Ihrem Header einfügen, und es ist nicht erforderlich, ihn in die .c-Datei einzufügen. Includes sollten nach dem #define stehen, damit sie nicht unnötigerweise mehrfach enthalten sind. Beispielsweise:

/* myHeader.h */
#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <glib.h>

struct S
{
    gchar c;
};

#endif /* MY_HEADER_H */

und

/* myCode.c */
#include "myHeader.h"

void myFunction()
{
    struct S s;
    /* really exciting code goes here */
}
0
danio

Normalerweise schützen Bibliotheksentwickler ihre Includes mit dem "Trick" #ifndef/# define/#endif vor Mehrfacheinschlüssen, damit Sie dies nicht tun müssen.

Natürlich solltest du das überprüfen ... aber auf jeden Fall wird der Compiler es dir irgendwann sagen ;-) Es ist auf jeden Fall eine gute Praxis, nach mehreren Einschlüssen zu suchen, da dies den Kompilierungszyklus verlangsamt.

0
jldupont

Fügen Sie einfach alle externen Header in eine gemeinsame Header-Datei in Ihr Projekt ein, z. global.h und füge es in all deine c-Dateien ein:

Es kann so aussehen:

#ifndef GLOBAL_GUARD
#define GLOBAL_GUARD

#include <glib.h>
/*...*/
typedef int  YOUR_INT_TYPE;
typedef char YOUR_CHAR_TYPE;
/*...*/
#endif

Diese Datei verwendet include guard, um mehrfache Einschlüsse, illegale Mehrfachdefinitionen usw. zu vermeiden.

0
psihodelia