Eine C-Quelldatei in eine andere aufnehmen?


Antworten:


113

Bei richtiger Anwendung kann dies eine nützliche Technik sein.

Angenommen, Sie haben ein komplexes, leistungskritisches Subsystem mit einer relativ kleinen öffentlichen Schnittstelle und viel nicht wiederverwendbarem Implementierungscode. Der Code umfasst mehrere tausend Zeilen, ungefähr hundert private Funktionen und ziemlich viele private Daten. Wenn Sie mit nicht trivialen eingebetteten Systemen arbeiten, werden Sie wahrscheinlich häufig genug mit dieser Situation umgehen.

Ihre Lösung wird wahrscheinlich geschichtet, modular und entkoppelt sein, und diese Aspekte können sinnvoll dargestellt und verstärkt werden, indem verschiedene Teile des Subsystems in verschiedenen Dateien codiert werden.

Mit C können Sie viel verlieren, wenn Sie dies tun. Fast alle Toolchains bieten eine anständige Optimierung für eine einzelne Kompilierungseinheit, sind jedoch in Bezug auf alles, was als extern deklariert wird, sehr pessimistisch.

Wenn Sie alles in ein C-Quellmodul packen, erhalten Sie -

  • Leistungs- und Codegrößenverbesserungen - Funktionsaufrufe werden in vielen Fällen eingebunden. Auch ohne Inlining hat der Compiler die Möglichkeit, effizienteren Code zu erstellen.

  • Daten und Funktionen auf Verbindungsebene werden ausgeblendet.

  • Vermeidung von Verschmutzung durch Namespaces und deren Folgen - Sie können weniger unhandliche Namen verwenden.

  • Schnellere Zusammenstellung und Verknüpfung.

Aber Sie bekommen auch ein unheiliges Durcheinander, wenn es darum geht, diese Datei zu bearbeiten, und Sie verlieren die implizite Modularität. Dies kann überwunden werden, indem die Quelle in mehrere Dateien aufgeteilt und diese eingeschlossen werden, um eine einzige Kompilierungseinheit zu erstellen.

Sie müssen jedoch einige Konventionen festlegen, um dies ordnungsgemäß zu verwalten. Diese hängen in gewissem Maße von Ihrer Toolchain ab, aber einige allgemeine Hinweise sind:

  • Fügen Sie die öffentliche Schnittstelle in eine separate Header-Datei ein - Sie sollten dies trotzdem tun.

  • Haben Sie eine Haupt-C-Datei, die alle Neben-C-Dateien enthält. Dies könnte auch den Code für die öffentliche Schnittstelle enthalten.

  • Verwenden Sie Compiler Guards, um sicherzustellen, dass private Header und Quellmodule nicht von externen Kompilierungseinheiten enthalten sind.

  • Alle privaten Daten und Funktionen sollten als statisch deklariert werden.

  • Behalten Sie die konzeptionelle Unterscheidung zwischen .c- und .h-Dateien bei. Dies nutzt bestehende Konventionen. Der Unterschied besteht darin, dass Ihre Header viele statische Deklarationen enthalten.

  • Wenn Ihre Toolchain keinen Grund dafür darstellt, benennen Sie die privaten Implementierungsdateien als .c und .h. Wenn Sie Include-Wachen verwenden, erzeugen diese keinen Code und führen keine neuen Namen ein (möglicherweise werden während der Verknüpfung einige leere Segmente angezeigt). Der große Vorteil ist, dass andere Tools (z. B. IDEs) diese Dateien angemessen behandeln.


1
+1 Dies ist immer noch die Realität, während bessere Compiler diese Methode mit der Zeit überflüssig machen. GCC 4.5 mit Link-Time-Optimierung ist ein großer Schritt auf dem Weg.
u0b34a0f6ae

Ich habe so viele Programme gesehen, die dies tun, und es geht mir auf die Nerven, wenn ich versuche, ihren Code wiederzuverwenden. Vielen Dank, dass Sie erklärt haben, warum sie es tun, und die Verwendung einer Wache vorgeschlagen haben (was oft nicht getan wird).
Joey Adams

In der Embedded-Entwicklung für C51 hat die Verwendung von externen und mehreren C-Dateien nur Kopfschmerzen verursacht. Ich wechsle zu einer C-Datei, die alle anderen enthält, um meine Zeit zurückzugewinnen.
Mahesh

Für die Datensätze: GCC unterstützt Optimierung über verschiedene Übersetzungseinheiten finden diese SO Gewinde und GCC Abschnitt zu Link Zeitoptimierung .
Twonky

60

Ist es o.k? Ja, es wird kompiliert

wird es empfohlen? no - .c - Dateien werden zu .obj - Dateien kompiliert, die nach dem Kompilieren (durch den Linker) in die ausführbare Datei (oder Bibliothek) verknüpft werden, sodass keine .c - Datei in eine andere aufgenommen werden muss. Was Sie wahrscheinlich stattdessen tun möchten, ist, eine .h-Datei zu erstellen, die die in der anderen .c-Datei verfügbaren Funktionen / Variablen auflistet und die .h-Datei enthält


14
Es ist auch erwähnenswert, dass selbst wenn es kompiliert wird, es möglicherweise nicht verknüpft wird, wenn die # eingeschlossene .c-Datei ebenfalls kompiliert wird und die beiden Objektdateien miteinander verknüpft sind. Dies kann zu mehrfach definierten Symbolen führen.
Nick Meyer

1
Eine kleine Frage. Ich habe eine Header-Datei, die eine struct + -Methode deklariert, und auch die entsprechende .c-Datei, um sie zu definieren. Wenn diese Methode die Struktur als Parameter verwendet, wie würde ich vermeiden, die C-Datei in eine andere C-Datei aufzunehmen, in der die Hauptmethode definiert ist?
Stdout

12

Nein.

Abhängig von Ihrer Build-Umgebung (die Sie nicht angeben) stellen Sie möglicherweise fest, dass sie genau so funktioniert, wie Sie es möchten.

Es gibt jedoch viele Umgebungen (sowohl IDEs als auch viele handgefertigte Makefiles), in denen erwartet wird, dass * .c kompiliert wird. In diesem Fall treten wahrscheinlich Linkerfehler aufgrund doppelter Symbole auf.

Diese Praxis sollte in der Regel vermieden werden.

Wenn Sie die Quelle unbedingt einschließen müssen (und dies im Allgemeinen vermieden werden sollte), verwenden Sie ein anderes Dateisuffix für die Datei.


9

Ich dachte, ich würde eine Situation teilen, in der mein Team beschlossen hat, .c-Dateien aufzunehmen. Unsere Architektur besteht größtenteils aus Modulen, die über ein Nachrichtensystem entkoppelt sind. Diese Nachrichtenhandler sind öffentlich und rufen viele lokale statische Worker-Funktionen auf, um ihre Arbeit zu erledigen. Das Problem trat auf, als versucht wurde, eine Abdeckung für unsere Unit-Testfälle zu erhalten, da die einzige Möglichkeit, diesen privaten Implementierungscode auszuführen, indirekt über die öffentliche Nachrichtenschnittstelle bestand. Da einige Arbeiterfunktionen knietief im Stapel liegen, stellte sich heraus, dass dies ein Albtraum war, um eine angemessene Abdeckung zu erreichen.

Durch die Aufnahme der .c-Dateien konnten wir das Zahnrad in der Maschine erreichen, die wir testen wollten.


6

Sie können den gcc-Compiler unter Linux verwenden, um zwei c-Dateien in einer Ausgabe zu verknüpfen. Angenommen, Sie haben zwei c-Dateien, eine ist 'main.c' und eine andere ist 'support.c'. Der Befehl, diese beiden zu verbinden, lautet also

gcc main.c support.c -o main.out

Auf diese Weise werden zwei Dateien mit einer einzigen Ausgabe main.out verknüpft. Um die Ausgabe auszuführen, lautet der Befehl

./main.out

Wenn Sie die Funktion in main.c verwenden, die in der Datei support.c deklariert ist, sollten Sie sie in main auch mit der externen Speicherklasse deklarieren.


5

Die Erweiterung der Datei spielt für die meisten C-Compiler keine Rolle, daher funktioniert sie.

Abhängig von Ihren Makefile- oder Projekteinstellungen kann die enthaltene c-Datei jedoch eine separate Objektdatei generieren. Beim Verknüpfen kann dies zu doppelt definierten Symbolen führen.


3

Sie können .C- oder .CPP-Dateien ordnungsgemäß in andere Quelldateien einfügen. Abhängig von Ihrer IDE können Sie Doppelverknüpfungen normalerweise verhindern, indem Sie sich die Eigenschaften der Quelldateien ansehen, die Sie einschließen möchten. Klicken Sie dazu mit der rechten Maustaste darauf und klicken Sie auf Eigenschaften, und deaktivieren / aktivieren Sie Kompilieren / Verknüpfen / Ausschließen vom Build oder einer beliebigen Option vielleicht. Oder Sie konnten die Datei nicht in das Projekt selbst aufnehmen, sodass die IDE nicht einmal weiß, dass sie existiert, und nicht versucht, sie zu kompilieren. Und mit Makefiles würden Sie die Datei einfach nicht zum Kompilieren und Verknüpfen einfügen.

EDIT: Entschuldigung, ich habe es zu einer Antwort gemacht, anstatt auf andere Antworten zu antworten :(


1

Die C-Sprache verbietet diese Art von #include nicht, aber die resultierende Übersetzungseinheit muss noch gültig sein. C.

Ich weiß nicht, welches Programm Sie mit einer PRJ-Datei verwenden. Wenn Sie etwas wie "make" oder Visual Studio oder was auch immer verwenden, stellen Sie einfach sicher, dass Sie die Liste der zu kompilierenden Dateien ohne die Liste festlegen, die nicht unabhängig kompiliert werden kann.


0

Das Einfügen einer C-Datei in eine andere Datei ist legal, aber nicht ratsam, es sei denn, Sie wissen genau, warum Sie dies tun und was Sie erreichen möchten.
Ich bin mir fast sicher, dass, wenn Sie hier den Grund veröffentlichen, warum die Community hinter Ihrer Frage einen anderen geeigneteren Weg findet, um Ihr Ziel zu erreichen (bitte beachten Sie das "fast", da dies angesichts des Kontexts möglicherweise die Lösung ist ).

Übrigens habe ich den zweiten Teil der Frage verpasst. Wenn die C-Datei in einer anderen Datei enthalten ist und gleichzeitig in das Projekt aufgenommen wird, tritt wahrscheinlich ein Problem mit doppelten Symbolen auf, warum die Objekte verknüpft werden, dh dieselbe Funktion wird zweimal definiert (es sei denn, sie sind alle statisch).


-6

Sie sollten einen Header wie diesen hinzufügen

#include <another.c>

Hinweis: Beide Dateien sollten an derselben Stelle abgelegt werden

Ich benutze dies in Codevison AVR für ATMEGA-Mikrocontroller und arbeite wirklich, aber es funktioniert nicht in normalen C-Sprachdateien

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.