Hi, habe folgende Frage. Möchte ein Programm schreiben sagen wir "main.c" . Nun möchte ich ein zweites C-File haben wie "Library.c" worin ich konstante Werte definieren möchte. darin deklariere und initialisiere ich nun die Werte wie z.B. "const int Wert_A = 5;" im dazugehörigen Header "Library.h" schreibe ich nun extern const int Wert A; und binde dieses Headerfile in die "main.c" per #include ein. Somit kann ich den "Wert_A" auch in "main.c" benutzen. Kann das jemand bestätigen oder gegebenenfalls sagen wie es richtig ist? mfg
Ja, das sieht korrekt aus. Du musst natürlich daran denken, Deine Datei "library.c" zum Projekt hinzuzufügen - also ins Makefile eintragen oder der Projektverwaltung Deiner Entwicklungsumgebung hinzufügen, damit die Datei auch vom Compiler übersetzt und vom Linker zum fertigen Programm gelinkt wird.
Hi, Du legst auf diese Art eine Variable an, die den Wert 5 erhält. Wenn Wert_A benutzt wird, wird auch diese Variable aus dem RAM geladen. Die andere Möglichkeit ist die Definition als "echte" Konstante: im Headerfile einfach deklarieren: #define Wert_A 5 Der Unterschied: Bei Berechnungen wird nicht die Variable aus dem RAM geholt, sondern eine Konstante in den Assemblercode eingesetzt. Du sparst Dir den RAM-Platz der Variable. Zusätzlich kann der Compiler optimieren: so würde: if (Wert_A == 5){ while(1); } zu while(1); optimiert werden, weil der Compiler weiss, dass if (Wert_A == 5) immer wahr ist. Gruß, Stefan
@stefan ich dachte immer, const int ivar=1 wird durch optimierung so behandelt, als ob da #define ivar 1 steht. nur zusätzlich findet bei const int eine typprüfung statt. cu
@keks: Eine #define-äquivalente Behandlung geht aber nur, wenn der Compiler zur Übersetzungszeit des Moduls den Wert der Konstanten kennt. Wird die aber erst nach der Übersetzungszeit zum Modul gelinkt (und darum geht es hier ja), dann kann prinzipiell nicht wie bei einem #define verfahren werden.
müsste so funzen... an der stelle, an der der header eingefügt ist, linkt der linker später die dort eingetragenen sachen hin, und diese, die wiederum im header verlinkt sind... da du dies vor dem main(... machst, sind alle im header definierten sachen also auch global bekannt...
habs probiert. const int ivar=3; if (ivar==3) asm("nop"); wird mit der option -Os auf ein einfaches "nop" reduziert. daher werde ich const <type> der #define bevorzugen.
@keks: Wenn beides in einem Sourcefile steht, dann geht das so, aber es geht nicht, wenn die const-"Variable" aus einem anderen Modul gelinkt wird.
@rufus ich meine, wir schreiben in Library.h nicht #define Wert_A 5 sondern static const int Wert_A = 5; das static führt zur internen bindung. damit ist Wert_A nur in den dateien bekannt, in denen #include "Library.h" steht und der linker stört sich nicht über mehrfache deklaration. bei nur einer objektdatei reicht dann natürlich ein const int ... .
Nö, es war die Rede davon, daß in "library.h" extern const int Wert_A; steht. Zumindest schrieb dies "jungspunt" im ersten Beitrag dieses Threads.
mit static const int Wert_A=5 wollt ich nur zeigen, dass man nicht #define braucht, um RAM zu sparen.
Erstmal danke für die Info, ich meinte es wirklich so, dass im Header nur extern const Typ XX steht und die Initialisierung im dazu gehörigen C-File stattfindet. gruß Jungspunt
1 | static const int Wert_A=5; |
Das gehört ganz sicher nicht in ein Headerfile. Der GCC "überlagert" zwar das Vorhandensein bei Mehrfachdefinition, das ist aber GCC-Typisch und nicht irgendein Standard. Daß hier mehrfach definiert werden kann, liegt einzig und allein an der Häufigkeit des Einbindens von "library.h". Der richtige Weg ist der von "Jungspunt" erläuterte, was aber zu dem von Rufus geschilderten Problem führen wird.
Patrick, in diesem Falle dürfte die Definition der Variablen in der Headerdatei zu keinem Problem führen - da sie als static deklariert ist, ist sie nur lokal (innerhalb einbindender Module) sichtbar und wird nicht als Symbol für den Linker exportiert. Das von Dir angesprochene gcc-spezifische Handling von Mehrfachdefinitionen auf Linkerebene kommt hier also gar nicht zum Zug, damit es das täte, müsste der "storage class specifier" static entfernt werden.
Hups, das static hatte ich nicht gesehen. Na dann ist das ganze ja noch schlimmer, und es handelt sich in mehreren einbindenden C-Files tatsächlich um jeweils unterschiedliche Speicherstellen...ich werde das gleich mal prüfen.
Und wenn Dein Compiler dafuer tatsaechlich eine (konstante) Variable anlegt (ohne dass Du irgendwo die Adresse davon nimmst), dann schmeiss ihn gleich weg.
Also, hier mal der Code: static.c:
1 | #include <stdio.h> |
2 | #include "static.h" |
3 | |
4 | int
|
5 | main(void) |
6 | {
|
7 | printf("%s: location of 'Wert_A' is %p (%d)\n", |
8 | __FUNCTION__, &Wert_A, Wert_A); |
9 | |
10 | return foo(); |
11 | }
|
static2.c
1 | #include <stdio.h> |
2 | #include "static.h" |
3 | |
4 | int
|
5 | foo(void) |
6 | {
|
7 | printf("%s: location of 'Wert_A' is %p (%d)\n", |
8 | __FUNCTION__, &Wert_A, Wert_A); |
9 | |
10 | return 0; |
11 | }
|
static.h:
1 | static const int Wert_A = 5; |
2 | |
3 | extern int foo(void); |
Ergebnis: main: location of 'Wert_A' is 00403000 (5) foo: location of 'Wert_A' is 00403040 (5) Kompiliert auf Windows2000 mit gcc 3.4.2: gcc -g -O2 -Wall -o static.exe static.c static2.c
Das ist klar. Du weist den Compiler an, die Adresse der Konstanten auszugeben. Da bleibt dem Compiler nichts anderes uebrig als 2 Variablen dafuer anzulegen. Aber das tut man normalerweise nicht. Bei einer Konstanten interessiert normalerweise nur der Wert. Und in diesem Fall eliminiert der Compiler die Konstante vollstaendig und setzt den definierten Wert im Code dort ein wo er gebraucht wird. 'Constant Folding' ist fuer den Compiler eine der leichtesten Uebungen die es beim Optimieren gibt.
Naja, warum sollte man denn überhaupt eine definition in ein Headerfile auslagern? Das ergibt keinen Sinn. Eine der leichtesten Übungen eines Programmierers sollte es doch sein, sich an gewisse "Gepflogenheiten" zu halten, also macht man es eben so, daß im Headerfile die Deklaration mit extern versehen wird. Dann kommt es in keinem Fall zur "Doppeldefinition"... Jemand anderer Meinung?
> Naja, warum sollte man denn überhaupt eine definition in ein > Headerfile auslagern? Das ergibt keinen Sinn. Damit garamtiert ist, dass mehere Compilation-Units immer mit dem gleichen Zahlenwert fuer eine bestimmte Konstante compiliert werden. Das static macht man, damit diese Konstante im fertigen EXE keinen Platz verbraucht, sondern direkt im Code eingesetzt wird. > Eine der leichtesten Übungen eines Programmierers sollte es > doch sein, sich an gewisse "Gepflogenheiten" zu halten, also > macht man es eben so, daß im Headerfile die Deklaration mit > extern versehen wird. Dann kommt es in keinem Fall > zur "Doppeldefinition"... Du hast es immer noch nicht geschnallt. Hier geht es nicht um Doppeldefinition. Hier geht es darum, dass die Konstante keinen Speicherplatz fuer sich selbst verbraucht. Die konstante Variable wird ueberhaupt nicht im Speicher angelegt, sondern existiert als Variable nur waehrend des kompilierens. Durch die Konstruktion static const int Alpha = 5; ist es dem Compiler moeglich, ueberall im Source Code die Konstante Alpha durch den Wert 5 zu ersetzen. Dies geht auch dann, wenn die Definition in einem Header File steht und per #include eingebunden wird -> Alpha taucht im fertigen EXE ueberhaupt nicht auf! Ins Header File geb ich das ganze deshalb, damit alle 7 (um mal eine Zahl zu nennen) *.c Dateien immer denselben Wert fuer Alpha sehen und ich nur eine Stelle habe, an der ich den Zahlenwert bei Bedarf aendern muss.
also noch mal von vorn: * erst läuft der präprozessor über die source-files dieser verarbeitet alle zeilen, die mit # beginnen und ersetzt zum Beispiel alle Namen, die mit #define NAME (wert) definiert wurden durch den wert. Der Präprozessor nimmt hier nur ersetzungen vor, d.h. der compiler sieht bei den obrigen beispiel wirklich nur if(5 == 5) und nichts anderes!!! Auch werden die #include-Zeilen durch den vom präprozessor verarbeiteten inhalt der dateien ersetzt * dann kommt der compiler und macht aus dem code ein object-file mit machinen-code und externen verweisen * dann kommt der linker und löst die externen verweise auf. GENUG GELABERT... definitionen sollten nicht in header-dateien stehen. dort sollten nur deklarationen (funktions-prototypen, ...) und präprozessor anweisungen stehen. also besser #define Wert_A 5 ...... kleiner tip am rande: compiler sind von menschen, menschen sind alles andere als perfekt, also sind auch compiler nicht perfekt. Das anzeigen der Zwischen-Dateien (auch schon nach dem präprozessor-lauf) kann so manches problem lösen.
input: :::::::::::::: const2.c :::::::::::::: #include "const.h" int fkt1 (void) { return (ivar == 3) ? 1 : 0; } :::::::::::::: const.c :::::::::::::: #include "const.h" void main (void) { if (ivar == 3) asm ("nop"); fkt1 (); } :::::::::::::: const.h :::::::::::::: static const int ivar=3; build: scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... /usr/local/avr/bin/avr-gcc -o const.o -c -mmcu=atmega8 -ffreestanding -Os -g3 -I . const.c /usr/local/avr/bin/avr-gcc -o const2.o -c -mmcu=atmega8 -ffreestanding -Os -g3 -I . const2.c /usr/local/avr/bin/avr-gcc -o const const.o const2.o scons: done building targets. output: void main (void) { 52: cf e5 ldi r28, 0x5F ; 95 54: d2 e0 ldi r29, 0x02 ; 2 56: de bf out 0x3e, r29 ; 62 58: cd bf out 0x3d, r28 ; 61 if (ivar == 3) asm ("nop"); 5a: 00 00 nop fkt1 (); 5c: 01 d0 rcall .+2 ; 0x60 5e: 03 c0 rjmp .+6 ; 0x66 00000060 <fkt1>: 60: 81 e0 ldi r24, 0x01 ; 1 62: 90 e0 ldi r25, 0x00 ; 0 64: 08 95 ret 00000066 <_exit>: 66: ff cf rjmp .-2 ; 0x66 -> static const int blabla geht wohl. aus meinem c++-skript: Mit solchen Deklarationen (const int laenge=1024;) lassen sich viele #define-Konstanten vermeiden, die bekanntlich problematisch sind, weil sie sich der Syntaxprüfung des Compilers entziehen. const-Objekte sind implizit static (also nur innerhalb der Quelle gültig), ... . Das unterscheidet sie von normalen globalen Variablen, deren Voreeinstellung extern ist, und macht es problemlos möglich, die Deklarationen in Header-Dateien unterzubringen. wie oben zu sehen, geht das auch mit c, wobei bei c die variable als static deklariert werden muss. aber wo ist da der unterschied, oder gibt es überhaupt einen?
> wie oben zu sehen, geht das auch mit c, wobei bei c > die variable als static deklariert werden muss. aber > wo ist da der unterschied, oder gibt es überhaupt einen? O, da gibt es einen Unterschied, und zwar einen großen. Wäre die Variable nicht static, dann würde für die Variable ein Linkersymbol erzeugt werden, weil dann der Zugriff auch aus anderen Modulen möglich wäre. Daher kann ein ordnungsgemäß arbeitender C-Compiler (genauer: dessen Linker) ein solches Programm nicht übersetzen (genauer: linken), weil das betreffende Symbol mehrfach definiert ist. Ärgerlicherweise lässt gcc so etwas zu, in dem mehrfach definierte Symbole quasi "überlagert" werden, was den Anfängerfehler der Variablendefinition in Headerdateien erst ermöglicht.
Ich nehme an Du meinst den Unterschied zwischen C++ und C? Wie Du richtig sagst, muss man in C das static angeben. Ansonsten laeuft man in die Falle der 'One Definition Rule' (Doppeldefinition). Da solche Definitionen aber haeufig vorkommen, wurde das in C++ geaendert. const Definitionen verhalten sich dort so, also ob sie von Haus aus static waeren. Ansonsten gibt es keinen Unterschied. Wenn Du das Ganze also sowohl unter C als auch unter C++ benutzen moechtest, dann gib static immer an. Brauchst Du es nur fuer C++, dann kannst Du Dir das static sparen, der Compiler nimmt es fuer Dich an.
also mein gcc meckert, wenn ich obiges programm mit const int ivar=3; schreibe: const2.o(.data+0x0): In function `fkt1': const2.c:7: multiple definition of `ivar' const.o(.data+0x0): const.c:7: first defined here wie "überlagert" der gcc denn nun? ohne das static funktioniert es nur, wenn ich eine .c datei habe (logen). nochmal: Jungspund wollte wissen, ob const int + extern const int funktioniert. da sind wir uns einig, das funzt. jetzt kommt Stefan und sagt, nimm #define, das spart RAM. auch hier wieder keine einwände allerseits. nun sage ich, nimm static const int. das funktioniert genau wie #define, spart also RAM und gibt mir zusätzlich eine typprüfung. vielleicht sollten wir die meinungen dreier informatikprofessoren hierzu einholen. mal sehen, was die bevorzugen würden. cu
Deine Analyse unter 'nochmal' ist doch vollstaendig richtig. Wozu Informatikprofessoren? Die verstehen das doch selber nicht. Wenn schon, dann hoechstens Assistenten. Zum gcc kann ich nichts sagen. Selbst wenn der diese Erweiterung hat, wuerde ich sie nicht verwenden, sondern gleich richtig machen. Ist auf lange Sicht immer besser.
das mit dem "überlagen" war eigentlich ironisch, denn mein gcc scheint alles richtig zu machen.
Es gibt einen Anwendungsbereich, "extern const int" anstelle "static const int" zu verwenden, nämlich dann, wenn das so definierte Symbol zur Konfiguration einer Library verwendet werden soll. In diesem Falle muss das Programm, das die Library verwendet, dieses Symbol der Library zur Verfügung stellen, es ist aber nicht erforderlich, die Library neu zu übersetzen, was ja auch nicht immer möglich ist, beispielsweise, wenn kein Sourcecode der Library vorliegt. Natürlich kann in diesem Falle der Compiler die Optimierungen, wie sie oben angesprochen wurden, nicht durchführen. Das gcc-Verhalten sprach Patrick an (http://www.mikrocontroller.net/forum/read-1-286669.html#288169), welche Compileroption zu verwenden ist, damit das erfolgt, entzieht sich glücklicherweise? meiner Kenntnis. Karl Heinz ist in Bezug auf Informatikprofessoren nur Recht zu geben, die haben von vielem Ahnung, aber nicht von real existierenden Programmiersprachen. C oder C++ können/kennen die wenigsten davon. Ältere Semster fahren auf Oberon ab (wer nichts wird, wird Wirth) ...
um die ehre der profs. die können das sicherlich besser als der durchschnitt unserer forenteilnehmer. und in der tat würde ich einen assistenten fragen. der weiss es sicherlich und hat die antwort im kopf.
>Das gcc-Verhalten sprach Patrick an >(http://www.mikrocontroller.net/forum/read-1-286669.html#288169), >welche Compileroption zu verwenden ist, damit das erfolgt, entzieht >sich glücklicherweise? meiner Kenntnis. Da steht static const int Wert_A=5; das geht natürlich immer im Header, wird halt in jedem C File eine Variable Wert_A statisch angelegt (in wirklichkeit dann nicht, weil der Compiler die Konstante in den Code reintut). Was mit überlagern gemeint ist wäre int Wert_A; in mehreren C-Files (vonmiraus über den Header). Das geht leider wie oben erwähnt. Könnte am -std=gnu99 liegen. Erschreckend für mich, hab aber noch nix gefunden, in dem man das abstellen könnte. Müsste aber eine Linkeroption sein, weil der Compiler da ja nicht draufkommen kann weil er ja immer nur 1 File sieht beim compilieren.
Aja, ich hab mir sowieso angewöhnt Variablen die ich exportieren will so in das Headerfile zu schreiben (dass dann von jedem C File inkludiert wird):
1 | #ifndef _FTERM_H
|
2 | #define _FTERM_H
|
3 | |
4 | #ifdef FTERMMAIN
|
5 | #define EXTERN /**/ |
6 | #else
|
7 | #define EXTERN extern
|
8 | #endif
|
9 | |
10 | EXTERN int baudrate, showhex, showmaster, showslave, crcerrors; |
11 | /* Function prototypes */
|
12 | |
13 | void saveconfig (void); |
14 | int quit_program (GtkWidget * widget, GdkEvent * event, gpointer |
15 | user_data); |
16 | |
17 | #endif /* _FTERM_H */ |
Wobei im File das main() enthält steht: #define FTERMMAIN #include "fterm.h" in allen anderen fehlt natürlich das #define FTERMMAIN Nachteil ist aber, dass man nicht EXTERN int count=1; schreiben kann, man muss halt im Hauptprogramm initialisieren. Dadurch kann nix überladen werden und man hat den Überblick was man alles exportiert.
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.