Hi, ich hab ein Problem mit der Variablendefinition im Headerfile. Das Projekt ist eine digitale Uhr (eigentlich ein Teilprojekt). Die Uhrzeit wird in globalen Variablen in der Uhr.h gespeichert. Stellen kann man die Uhr über die RS232 (z.B. ATMEga IDE -> Debug -> RS232 -> Uhr stellen). Später soll das über eine DCF77 Platine von Reichelt und alternativ den HTML Header geschehen. Das Problem ist, mal müssen die globalen Variablen in der Uhr.h mit "extern" und mal ohne definiert sein. Ändert man was am Quellcode (z.b. in der main.cpp) und kompiliert neu, kommt mal Fehler 1 und mal Fehler 2. Manchmal gehts auch mehrmals nacheinander gut durch. Hängt wohl irgendwie an internen Verknüpfungen der obj Dateien. (ist auch so wenn man nichts an Code ändert der direkt mit der Uhr zu tun hat) Fehler 1 (mit export): .obj/main.o: In function `Update_Anzeige()': C:\Developing\AVR\Test\Digital Uhr/main.cpp:78: undefined reference to `Update_Datum' ... (usw. auch für die andern Variablen) Fehler 2 (ohne export): .obj/Uhr.o: In function `__vector_11': C:\Developing\AVR\Test\Digital Uhr/Uhr.cpp:11: multiple definition of `Update_Datum' .obj/main.o:C:\Developing\AVR\Test\Digital Uhr/main.cpp:74: first defined here... Zum testen des Problems gibts in der Uhr.h die Zeile //#define mit_export Das Display hängt etwas durcheinander an Port B und D an nicht aufeinanderfolgenden Pins. Die Pins sind aber im .h konfigurierbar. Im Sekundentakt blinkt noch eine LED. Controller ist ein ATMega 168. WinAVR 25.05.2007 Windows Vista ATMega IDE 2007 (Fehler tritt aber auch bei direktem Aufruf von make auf) Ein Preview der IDE gibts unter http://www.KarstenDonat.de/AVR als Download. Danke schonmal. Ciao Karsten http://www.KarstenDonat.de/AVR
Das Problem ist, dass wenn Du im Header-File direkt Variablen definierst (also ohne extern) und diese dann in verschiedenen Dateien einbindest, werden in jedem Modul die Variablen neu erstellt und er Linker hat dann Probleme, dass zusammenzuführen. --> Fehler 2 Die einzig sinnvolle Methode ist, die Variablen (alle) als extern in den Headern zu deklarieren und die eigentliche Definition in irgendeinem Modul zu machen. Man unterscheidet Deklaration und Definition. Deklaration (mit extern) sagt dem Compiler nur, dass irgendwo eine Variable mit dem angegebenen Datentypen und Namen existiert. Definition (ohne extern) legt eine Variable mit diesem Namen im Speicher an. Der Linker, der dann Deine Module zusammenführt, bekommt Probleme, wenn zwei globale Variablen mit gleichem Namen definiert wurden. Das #include macht eigentlich nichts anderes, als den kompletten Inhalt der Header-Datei an der Stelle einzufügen, wo #include steht.
Eine einfache Möglichkeit solche Probleme zu vermeiden ist folgende Vorgehensweise: * Du legst globale Variablen grundätzlich in ein eigenes Header File aus. * Du benutzt ein Makro EXTERN, welches im Headerfile definiert wird, wenn ein derartiges Makro noch nicht definiert wurde * Du includierst dieses Headerfile überall, wo globale Variablen benutzt werden. Und zwar ohne dich um das Makro EXTERN zu kümmern, sprich das Headerfile definiert sich EXTERN selber * In dem C File, in dem die main() ist, wird das Headerfile mit den globalen Variablen ebenfalls includiert. Allerdings wird hier eine Vorgabe fpr EXTERN gemacht * Globale Variablen werden nicht im Headerfile initialisiert, sondern über Zuweisungen in main(). Konkret globals.h ---------
1 | #ifndef EXTERN
|
2 | #define EXTERN extern
|
3 | #endif
|
4 | |
5 | EXTERN int Global1; |
6 | EXTERN int Global2; |
7 | EXTERN char String1[80]; |
a.c ---
1 | #include "globals.h" |
2 | |
3 | void foo() |
4 | {
|
5 | Global1 = 5; |
6 | Global2 = 7; |
7 | }
|
main.c ------
1 | #define EXTERN
|
2 | #include "globals.h" |
3 | |
4 | int main() |
5 | {
|
6 | Global1 = 8; |
7 | }
|
Der Schluessel liegt beim Makro EXTERN. Es ermöglicht es, dass dieselben Zeilen C Code im Headerfile sowohl zu einer Deklaration als auch zu einer Definition werden können. Gibt es vor dem inkludieren des Headerfiles kein Makro für EXTERN, dann erzeugt sich das Headerfile selbst eines, indem sie EXTERN zu 'extern' expandiert. Damit wird EXTERN int Global1; zu extern int Global1; und damit zu einer Deklaration. Deklarationen kannst du in einem C-Programm beliebig viele haben, solange sie nur alle gleich lauten. Eine Deklaration teilt dem Compiler nur mit, dass etwas existiert und welchen Datentyp es hat. In main.c ist die Situation aber etwas anders. Hier wird das Makro EXTERN mit einem Leerstring vorbelegt. Das heist aber auch, dass im Headerfile kein EXTERN Makri mehr erzeugt wird und die Zeile EXTERN int Global1; expandiert in main.c (und nur dort) zu int Global1; also einer Definition. Eine Definition teilt dem Compiler ebenfalls mit, dass etwas existiert und den Datentyp dazu. Gleichzeitig ist eine Definition aber auch die Aufforderung an den Compiler, Speicherplatz dafür zu Reservieren. Deklaration = Das Teil existier irgendwo, heist aber so und so und ist vom Datentyp so und so Definition = Hier existiert das Teil tatsächlich. Eine Deklaration ist also nur ein Verweis darauf, dass die Variable irgendwo existiert, während eine Definition aussagt: und hier ist jetzt das bewusste Teil, auf welches alle Deklarationen verweisen. Folgerichtig kannst du beliebig viele Deklarationen (also die Dinger mit extern) haben aber immer nur 1 Definition. Obiges Schema garantiert dir das. Ach ja: Wird im globals.h irgendwat geändert, muss main.c auf jeden Fall immer mitcompiliert werden, denn dort werden die globalen Variablen ja angelegt.
Ich finde die Lösung mit den EXTERN Macro ist nicht so empfehlenswert, weil man damit alle globalen Variablen in einem Header-File zusammenfassen muß und weil immer alles sichtbar ist. Ich ziehe es normalerweise vor, globale Variablen im dem Module zu definieren in das sie logischerweise gehören, z.B. pwm.c -> Funktionen und globale Variablen zur Puls-Weiten-Modulation. pwm.h -> deklariert Funktionen und globale Variablen in pwm.c stepper.c -> Funktionen und globale Variablen zu Schrittmotoren stepper.h -> deklariert Funktionen und globale Variablen in stepper.c main.c -> includiert pwm.h und stepper.h Beim Kompilieren von pwm.c brauchen und sollen die restlichen globalen Variablen nicht sichtbar sein. Größere Projekte mit C/C++ funktionieren sowieso nur auf diese Art, weil man sonst bei der kleinsten Änderung an den globalen Variablen immer alle Files neu kompilieren müsste. Klaus
Vielen Dank Euch dreien für die schnellen Antworten. Ich hab jetzt extern im .h und im zugehörigen .cpp die Eigentliche Definition. Jetzt gehts. Wohl mal wieder so n typischer Anfängerfehler ;-) Mit dem Global ist für mich denk ich ungünstig da ich möglichst alles zu einem Modul in den entsprechenden .h und .cpp Dateien haben will. Unsicher bin ich mir noch mit Konfigurationseinstellungen. Die hab ich im Moment weitestgehend in die config.h geschubst. Eine andere Idee wäre die main.h. Ciao Karsten
Wenn Variablen nur innerhalb eines Modules genutzt werden, dann gehört deren Definition in die *.c/*.cpp-Datei und gar nichts in die Headerdatei. Auch empfiehlt es sich, in solchen Fällen die Variablen als "static" zu deklarieren, damit die Variablen für den Linker nicht sichtbar sind.
Klaus Falser wrote: > Ich finde die Lösung mit den EXTERN Macro ist nicht so empfehlenswert, > weil man damit alle globalen Variablen in einem Header-File > zusammenfassen muß und weil immer alles sichtbar ist. Du redest von globalen Variablen an sich, bzw. dem vorgeschlagenen Mechanismus: "Alle Globalen in ein Headerfile". Das EXTERN Makro ist lediglich ein Mechanismus um globale Variablen mit geringem Aufwand handhabbar zu kriegen. Ob die jetzt in einem Headerfile oder in mehreren sind, ändert nichts am Mechanismus. > > Ich ziehe es normalerweise vor, globale Variablen im dem Module zu > definieren in das sie logischerweise gehören, > z.B. > > pwm.c -> Funktionen und globale Variablen zur Puls-Weiten-Modulation. > pwm.h -> deklariert Funktionen und globale Variablen in pwm.c > > stepper.c -> Funktionen und globale Variablen zu Schrittmotoren > stepper.h -> deklariert Funktionen und globale Variablen in stepper.c > > main.c -> includiert pwm.h und stepper.h > > Beim Kompilieren von pwm.c brauchen und sollen die restlichen globalen > Variablen nicht sichtbar sein. Du kannst dasselbe Schema mit dem EXTERN Makro machen. Ob die globalen Variablen in einem Headerfile zusammengefasst sind, oder wie du anmerkst nach Modulen geordnet in eigene Headerfiles kommen, ändert nichts am Mechanismus. Grundsätzlich sollte man überhaupt keine globalen Variablen benutzen. Allerdings wird diese 'eherne Regel' der Programmierung für µC etwas aufgeweicht, weil man ja schliesslich auch noch ein paar Restriktionen durch die Plattform hat und nicht so urassen kann, wie auf einem Desktop System.
Wie gesagt, mir gefällt die Version mit dem EXTERN nicht. Meine Gründe sind : - Globale Variablen gehören nicht in ein einziges Quellfile, sondern zu dem Quellfile in das sie funktionell gehören. - Es ist nicht so viel Aufwand, die Variablen im Quellfile nocheinmal anzuführen. Außerdem sind sie dann dort für den beim Programmieren sichtbar und hinter einem #include versteckt. - Bei initialisierten globalen Variablen funktiert der Mechanismus nicht (höchstens mit Trickserei) - Ich liebe hierarchische Designs : z.B. main.c verwendet Funktionen und globale Variablen aus stepper.c, includiert deshalb stepper.h stepper.c verwendet Funktionen und globale Variablen aus hardware.c, includiert deshalb hardware.h und stepper.h (zur Kontrolle). hardware.h includiert hardware.h (zur Kontrolle). Wenn ich den Mechanismus mit dem EXTERN verwenden würde, müßte ich in main.c beide Header-Files includieren. main.c : #define EXTERN #include "stepper.h" #include "hardware.h" ... main.c würde auch von Hardware.h abhängen und müßte bei einer Änderung von Hardware.h neu kompiliert werden. Das macht zwar bei kleinen Designs nichts aus, ist aber bei größeren Designs unerwünscht (und geht dem Prinzip einer Hierarchie zuwider). Alternativ könnte man das stepper.c schreiben #define EXTERN #include "stepper.h" #undef EXTERN #include "hardware.h" #include .... --> Gefällt mir auch nicht. Klaus
Eine Möglichkeit wäre es, das Macro "EXTERN" je nach Headerdatei anders zu benennen, und zwar den Dateinamen des zugehörigen Sourcefiles zu verwenden: stepper.c: #define STEPPER_C #include "stepper.h" #include "blafusel.h" ... stepper.h: #ifndef STEPPER_C #define EXTERN extern #endif EXTERN int irgendwas;
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.