Ihr Fall scheint ein Grenzfall zu sein, aber eine Include-Datei kann als eine Art Vertrag zwischen dieser Quelldatei und anderen Quelldateien angesehen werden, die diese Funktionen benötigen.
Indem Sie den "Vertrag" in eine Header-Datei schreiben, können Sie sicherstellen, dass die anderen Quelldateien wissen, wie diese Funktionen aufgerufen werden, oder Sie werden vielmehr sicher sein, dass der Compiler eingefügt wird der richtige Code und überprüfen Sie seine Gültigkeit zum Zeitpunkt der Kompilierung.
Was aber, wenn Sie dann (versehentlich) den Funktionsprototyp in der entsprechenden Quelldatei geändert haben?
Indem Sie in diese Datei den gleichen Header wie alle anderen einfügen, werden Sie bei der Kompilierung gewarnt, sollte eine Änderung versehentlich den Vertrag "brechen".
Update (aus @ tmlens Kommentar): Auch wenn dies nicht der Fall ist, kann eine Include-Datei auch Deklarationen und Pragmas wie #defines, typedef, enum, struct und inline verwenden als Compiler-Makros würde es keinen Sinn machen, mehr als einmal zu schreiben (eigentlich wäre das gefährlich an zwei verschiedenen Stellen zu schreiben, damit die Kopien nicht mit schlechten Ergebnissen zusammenfallen). Einige davon (z. B. ein Pragma-Struktur-Pragma ) könnten zu schwer zu erkennenden Fehlern werden.
Dies ist nützlich, weil Funktionen deklariert werden können, bevor sie definiert werden.
So kommt es, dass Sie die Deklaration haben, gefolgt von einem Aufruf \ Aufruf, gefolgt von der Implementierung. Das musst du nicht, aber du kannst es.
Die Header-Datei enthält die Deklarationen. Sie können jederzeit aufrufen, solange der Prototyp übereinstimmt. Und solange der Compiler vor dem Kompilieren eine Implementierung findet.
Ich verstehe, dass wenn eine Quelldatei Funktionen aus einer anderen Datei referenzieren muss, sie ihre Header-Datei enthalten muss, aber ich verstehe nicht, warum die Quelldatei eine eigene Header-Datei enthält. Der Inhalt in der Header-Datei wird einfach kopiert und als Funktionsdeklaration in die Quelldatei eingefügt. Für Quelldatei, die eine eigene Header-Datei, eine solche "Deklaration" scheint mir nicht notwendig, in der Tat, Projekt noch kompilieren und verknüpfen kein Problem nach dem Entfernen der Header aus der Quelldatei, so was ist der Grund für die Quelldatei gehören eigene Kopfzeile?
Der Hauptvorteil besteht darin, dass der Compiler die Konsistenz Ihrer Kopfzeile und ihrer Implementierung überprüft. Sie tun es, weil es bequem ist, nicht weil es erforderlich ist. Es kann durchaus möglich sein, das Projekt ohne diese Einbeziehung korrekt kompilieren und ausführen zu lassen, aber es kompliziert die Wartung Ihres Projekts auf lange Sicht.
Wenn Ihre Datei keine eigene Kopfzeile enthält, können Sie versehentlich in eine Situation geraten, in der die Vorwärtsdeklaration einer Funktion nicht mit der Definition der Funktion übereinstimmt - vielleicht weil Sie einen Parameter hinzugefügt oder entfernt haben und vergessen haben, die Kopfzeile zu aktualisieren . Wenn dies geschieht, würde der Code, der auf der Funktion mit Nichtübereinstimmung beruht, noch kompilieren, aber der Aufruf würde zu undefiniertem Verhalten führen. Es ist viel besser, wenn der Compiler diesen Fehler findet, was automatisch geschieht, wenn Ihre Quelldatei eine eigene Kopfzeile enthält.
Praktisches Beispiel - gehen Sie von folgenden Dateien in einem Projekt aus:
%Vor%Beachten Sie, dass die Deklaration in %code% nicht mit der Definition in %code% übereinstimmt; Die Rückgabetypen sind unterschiedlich. %code% ruft die Funktion %code% auf, vorausgesetzt, sie gibt eine %code% gemäß der Deklaration in %code% zurück.
%code% und %code% werden getrennt voneinander kompiliert. Da %code% %code% wie in %code% deklariert, kompiliert es erfolgreich. Da %code% nicht %code% enthält, ist dem Compiler der Typkonflikt zwischen der Deklaration und der Definition nicht bekannt, so dass er ebenfalls erfolgreich kompiliert wird.
Wenn Sie die beiden Objektdateien miteinander verknüpfen, stimmt der Maschinencode für den Funktionsaufruf nicht mit dem überein, was der Maschinencode für die Funktionsdefinition erwartet. Der Funktionsaufruf erwartet, dass ein %code% -Wert zurückgegeben wird, aber die Funktionsdefinition gibt %code% zurück. Dies ist ein Problem, insbesondere wenn die beiden Typen nicht die gleiche Größe haben. Im besten Fall erhalten Sie ein Müll-Ergebnis.
Wenn Sie %code% in %code% einfügen, kann der Compiler diese Abweichung erkennen, bevor Sie Ihr Programm ausführen.
Und, wie in einer früheren Antwort darauf hingewiesen wird, wenn %code% irgendwelche Typen oder Konstanten definiert, die von %code% verwendet werden, dann müssen Sie sie definitiv einbeziehen.
Die Header-Datei sagt den Leuten, was die Quelldatei tun kann.
Also muss die Quelldatei für die Headerdatei ihre Verpflichtungen kennen. Deshalb ist es enthalten.