Forum: Mikrocontroller und Digitale Elektronik Verhindern von Doppel-Include


von Harri (Gast)


Lesenswert?

Hallo,
wie verhindere ich doppel-Includierungen bei Verwendungung mehrer
C-Files, ohne den C-Code jedes mal zu änderen wenn ich ein bereits
geschriebenes "Programm" bzw. Funktionen verwenden möchte.
Danke für die Hilfe.
Gruß Harri

Verwende IAR und manchmal ICC

von Rahul (Gast)


Lesenswert?

Wenn man in der ersten Include-Datei ein Makro definiert, kann man in
allen folgenden mit #ifdef abfragen, ob das entsprechende Makro schon
definiert und somit die Datei schon eingebunden wurde.

#ifdef und #ifndef sind Standard-C-Elemente.

von Ingo (Gast)


Lesenswert?

Im Header schreibst Du jeweils

#ifdef MEIN_HEADER_NAME
#define MEIN_HEADER_NAME

... // der Rest des Headers

#endif

C-Dateien solltest Du an keiner Stelle inkludieren. Das ist sehr, sehr,
sehr, .... (n+1 mal, mit n->oo) schlechter Stil.

von Harri (Gast)


Lesenswert?

also im "main"-Teil z.B #include "temp.c" und #include "lcd.c"
einfügen und dann im alten File temp.c , da dieses auch das lcd.c
verwendet :
#ifndef #include "lcd.c"
einfügern, damit in main.c nicht zweimal lcd.c includiert wird?

von Rahul (Gast)


Lesenswert?

eher anders herum: in der main.c das #ifndef benutzen.
Ausprobieren könnte helfen...

von Harri (Gast)


Lesenswert?

Wie schlechter Stil?
Ich dachte der Vorteil von C ist die Wiederverwendbarkeit von bereits
geschriebenem Code?
Ist es dann besser per Copy and Paste alte Teile bzw. Methoden zu
integrieren?
Gruß Harri

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ingo hat recht. Das macht man nicht so.
.c-Dateien werden nicht nie niemals nirgendwo inkludiert.

Dazu gibt es makefiles und einen Linker.

IAR sollte dazu eine Projektverwaltung zur Verfügung stellen, die im
Handbuch beschrieben sein sollte.

von Rainer (Gast)


Lesenswert?

Hallo!

Für das direkte Einfügen von Dateien per Include, auch wenn es sich
dabei nicht um *.h Dateien handelt, muss ich mal eine Lanze brechen.

Auf großen System ist es angebracht und zeugt von gutem Stil, wenn nur
die Header eingefügt werden. Der Linker erledigt dann den Rest. Im
Bereich der Controller jedoch gibt es Compiler, die nicht über
Modulgrenzen hinweg optimieren können, so wie es z.B. beim cc5x gegeben
ist. Hier ist dann ein direktes Include von C-Dateien erlaubt, wobei
ggfs. auch Schleifen von Include-Anweisungen bei #ifdef/#define/#endif
notwendig ist.

Gruß,
Rainer

von Dirk D. (dirkd)


Lesenswert?

@Rainer:
Kannst Du das bitte an einem Beispiel erklären. Oder hast du Referenzen
dazu (Docs, Application Notes oder ähnliches)?

von Peter D. (peda)


Lesenswert?

"Im Bereich der Controller jedoch gibt es Compiler, die nicht über
Modulgrenzen hinweg optimieren können, so wie es z.B. beim cc5x
gegeben
ist. Hier ist dann ein direktes Include von C-Dateien erlaubt"


Dann sollte man trotzdem die Deklarationen in *.h-Files schreiben und
diese zu Anfang includieren und die eigentlichen *.C-Quelltexte
dahinter.


Peter

von Stromspannung (Gast)


Lesenswert?

Ingos Beschreibung ist vollkommen zutreffend. Die Dinger heißen auch
Headerguards. Und natürlich schreibt man diese in das jeweils zu
inkludierende File und nicht etwa dort, wo das File inkludiert wird...
Denn ein Header soll selbst prüfen, ob es schon einmal eingebunden
wurde. Rahuls Darstellung muss ich scharf widersprechen.

Das Inkludieren von c-Files im Sinne von "Code-Reuse" ist nicht zu
empfehlen, wenn nicht die von Rainer beschriebenen Einschränkungen
gelten. Modulare Programmierung ist das jedenfalls nicht mehr, weil es
alle Quelltextdateien über Gebühr hinaus miteinander verknüpft. Auf
"normalen" Systemen sollten Headers inkludiert werden und der
entsprechende Modulcode wird als Objektdatei dagegen gelinkt.

von Tom (Gast)


Lesenswert?

Hi,

also im jeweiligen .h file ein #define und dann nach dem define
abfragen. Wenn das nich definiert is dann #include...hab ich das
richtig verstanden?

Bsp:
in der lcd.h:
/....
#define LCD
/....

in der main.c:
#ifndef LCD
#include "lcd.h"
//...
#endif
//....

von Nils (Gast)


Lesenswert?

Hallo Tom,

ich finde es schöner so:

Im Header: lcd.h

#ifndef LCD_H
#define LCD_H
 /...
#endif


und in der main.c:

#include "lcd.h"

Das hat den Charme, dass man lcd.hh einbinden kann, wo man es braucht.
Man muß sich gar keine Gedanken mehr machen, wer es sonst noch
eingebunden haben könnte.

Sicher funktioniert Deine Lösung auch, aber logischer erscheint es mir,
in der entsprechenden Header-Datei dafür zu sorgen, dass sie nicht
mehrfach aufgerufen werden kann.

Gruß, Nils

von Tom (Gast)


Lesenswert?

Hi Nils,

achso jetz glaub ich hab ich kapiert, wie ihr das meint :). Wenn die
header schon einmal eingebunden wurde existiert ja das LCD_H schon,
also   überspringer er das beim nächsten #include "lcd.h".

Aber ist es eigentlich nicht egal ob ein Header 2 mal includiert wird?
Wo gibts da "probleme" wenn ich jetz z.b. ein projekt aus 3 c-files
hab und in jedem c-file das "lcd.h" includiere?
Ich glaub ich hab da was falsch verstanden :)...

Grüße

von Ithamar G. (antimon)


Lesenswert?

Also vielleicht mal zum prinzipiellen Verständnis, das ist mir nämlich
auch lange Zeit schwer gefallen, zu verstehen.

Wenn ein Projekt kompiliert wird, müssen alle Funktionen und ihre
Aufrufsparameter bekannt sein. Hast du nur eine Datei und definierst
die Hilfsfunktionen vor der main(), so ist das kein Problem - alle in
der main() verwendeten Funktionen sind beim Durchlaufen des Quellcodes
bekannt, damit passt das.

Jetzt kommen die Header ins Spiel. Da du deinene Code zwecks Übersicht
(und für die Wiederverwendung) normalerweise in extra Dateien
schreibst, gibts ein Problem. Denn in der main() werden Funktionen
verwendet, die in einer anderen Datei definiert sind. Zwar könnten die
Dateien vorher geparst und nach verwendeten Funktionen durchsucht
werden, aber das geschieht nicht.
Denn neben deinen .c-Files (die du dir mit nem Texteditor anschauen
kannst) gibts auch .o (also Objekt-Files), die schon kompiliert worden
sind und nicht so einfach mit nem Texteditor angeschaut werden können.
Das ist wie ne Black Box - da sind möglicherweise tolle Funktionen
drin, aber als Mensch weisst du nix davon, da die Datei nur vom
Computer gelesen werden kann.

Also gibts die .h - Files. Hier steht drin, welche Funktionen mit
welchen Parametern dem Programm zur Verfügung stehen. Wenn das Programm
mit der includierten .h kompiliert wird, freut sich der Compiler, weil
er weiss, die Funktionen gibts irgendwo.

Deine .c-Files musst du nicht includen - die fügst du einfach nur so
ins Projekt ein. Denn die werden zwar mitkompiliert, aber weiter nix.
Die könntest du auch auf nem anderen PC kompilieren und dann als
Object-Files einbinden. Denn der Code interessiert erst mal ned, solang
ned Fehler drin sind, der Compiler weisst ja welche Funktionen in dem
Code vorhanden sein müssen und ist zufrieden.

Erst zum Schluss passiert das Zusammenfügen: Der Linker bastelt aus den
fertig kompilierten Dateien dein Programm. Er verbindet die kompilierten
Module mit deinem main() und wenn alle Funktionen vorhanden sind, die
laut Header-Dateien vorhanden sein sollten, läuft
das Programm auch.

Jetzt noch zu deiner Frage, warum du "nicht einfach so" mehrmals eine
Header-Datei includen kannst.
Stell dir ein include wie ein Copy&Paste vor.

Du hast ne Headerdatei mit z.B."int funktion(int, char);" und fügst
die zweimal per Include ein, dann bekommst du sowas:

int funktion(int, char);
int funktion(int, char);

Zwar schauen die gleich aus, aber der Compiler ist dumm, er sieht zwei
gleichnamige Funktionen und weiss nicht was er machen soll. Deswegen
hilf ihm und schreib deine Header wie folgt:

#ifndef _FUNKTON_H
#define _FUNKTION_H
int funktion(int, char);
#endif

Damit wird beim ersten include geschaut: Ist _FUNKTION_H bereits
definiert? Ist es nicht, deswegen weitermachen. Als erstes wird dann
_FUNKTION_H definiert, dann der weitere Text geparst.
Ab dem zweiten Include ist _FUNKTION_H bereits definiert, also
überspringe folgenden Code.
Bedeutet: Der Compiler weiss ja bereits, welche Funktionen er verwenden
kann, einem intelligenten Menschen muss man ja auch nicht 10x sagen,
dass sein Schnürsenkel offen ist, das sollte er bereits beim ersten
Hinweis beheben.

Hoffe, das wurde jetzt ein wenig verständlicher...

von Mark S. (struberg)


Lesenswert?

Es haben hier beide Meinungen etwas für sich.

das
#ifndef GAXI
  #include "GAXi.h"
#endif

hat man früher (vor 1990) verwendet, um zu verhindern, daß der Compiler
das headerfile noch einmal einliest. Das hat früher viel Zeit beim
Compilieren erspart.

Das war allerdings in Zeiten, als 2MB noch eine extreme Seltenheit
waren und ein full compile auf einem 386DX33 (IBM Model95 um über
7000€) bis zu 20 Minuten gedauert hat...

von Stromspannung (Gast)


Lesenswert?

Aber das Inkludieren von Headerfiles hat mit dem Kompilieren erstmal
nichts zu tun, da Headers normalerweise (*) nur Deklarationen, aber
keine Definitionen enthalten. Ein vernünftiger Compiler/Make baut nur
die Quellen neu, die gegenüber ihrem Objectfile neuer sind.
Headerguards dienen nicht dazu bedingte Kompilationen zu provozieren
(was mit #ifdef/#ifndef natürlich auch geht). Sie sollen einzig und
allein das mehrfache Inkludieren von Headerfiles verhindern, da dieses
zwangsläufig zu einem Fehler (doppelte Deklaration) führen würde.
Narrensicher beschrieben von Ithamar.


(*) Es gibt Ausnahmen.

von Tom (Gast)


Lesenswert?

Hi ithamar,

danke für die ausführliche Info :). Ich dachte immer der Compiler macht
das alles alleine u desw is des sinnlos ;)..naja man lernt nie aus.
Najo werd jetz mal meine ganzen Header in ne #ifndef struktur packen
:).

Grüße

von Harri (Gast)


Lesenswert?

Also gut, vielen Dank.
Hab noch mal nachgesehen. Ich hab keine *.c Files includiert sondern
auch nur meine dazu erstellten *.h Dateien. War also doch nicht alles
so falsch was ich so geschrieben hab.

Der Compiler beschwerte sich darüber, dass ich bei der neuen "main"
die #include <io430x14x.h> verwende und bei der älteren lcd.c und
zugehörigen lcd.h noch von früher die #include <msp430x14x.h> drin
hatte.
Da gibts Überschneidungen die ich jetzt wohl umgehen kann.

Danke, Gruß Harri

von Michael Jungnickl (Gast)


Lesenswert?

Hallo,

inkludieren von C Quellcode (*.c Datei) kann durchaus sinnvoll sein.
Zum einen habt ihr dann nur eine Instanz eurer Software; nur die wird
getestet und ggf. verändert. Nötig ist dies jedoch nur, wenn ihr eine
schlechte Prjektumgebung wie den Platformbuilder für WinCe nutzen müsst
der den Adressraum in verschiedene Bereiche segmentiert. Es gibt dann
mehrere Instanzen der Software die sich gegenseitige abgrenzen. Dann
ist der Code im Bootloader nicht mehr im Image zugänglich und die
zusätzlichen Softwareprojekte (z.B. Konsolenanwendungen) brauchen den
Treiber nochmals! In dieser WinCE Architektur muss der Quellcode also
dreifach vorhanden sein.

Tschüß

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Hä? Das erscheint mir reichlich wirr. Wieso sollte man bei normaler
Arbeit mit einem C-Compiler und Linker nicht "nur eine Instanz [der]
Software" haben?
Was bitte ist für Dich eine solche "Instanz" und woran machst Du das
fest?
Und was mag ein Treiber mit dem includieren von C-Quelltext zu tun
haben, und vor allem: Was hat das alles mit Windows CE zu tun, das wie
jede andere Windows-Version auch bekanntlich dynamisch linken kann?

von Unbekannter (Gast)


Lesenswert?

@Rufus:

Windows CE ist bekanntlich von Micros~1. Was erwartest Du.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Da das hier alles ausführlich erklärt wird, kommt jetzt auch noch "mein
Senf":

Man darf in eigenem Code keine führenden Unterstriche bei der Namenwahl
verwenden. Das verbietet der Standard und ist nur den Entwicklern des
Compilers und denen der Library gestattet.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.