Hallo,
ich habe mal ne frage wie man es richtig macht.
ich habe eine file1.c/.h und file2.c/.h
Die funktionen, die ich für file1.c benutze, dekarliere ich in file1.h.
Wenn ich jetzt aber von file1 eine funktion in file2.c benutzen möchte,
muss ich die Funktion in file1.h dekalieren und in file2.h als extern
deklarieren, oder reicht es aus,die Funktion in file1.h als extern zu
deklarieren und file1.h in file2.c mit einbinden? Wird dann das extern
nicht überflüssig? da ich die header mit der deklaration ja schon
eingebunden habe?
Mark
> reicht es aus,die Funktion in file1.h als extern zu> deklarieren und file1.h in file2.c mit einbinden?
Das ist die übliche Methode.
In name.h werden alle Funktionen/Variablen/Makros deklariert, die name.c
exportiert. Alle anderen Dateien, die etwas von name.c brauchen,
includen dann name.h.
Ahso, eine Nachfrage doch noch.
Warum deklariere ich diese funktion denn dann als extern? Ich deklariere
eine Funktion file1.h und schreibe diese in file1.c.
Wenn ich file1.h jetzt in file2.c einbinde, dann ist die Funktion doch
auch bekannt, auch ohne extern.
Ich glaube hier bestand ein Missverständnis.
In einer Header Datei deklariert man die Funktionen, die man in der
zugehörigen (gleichnamigen) C Datei ausschreibt.
In deinem Fall könnte die Datei file2.c beide Header inkludieren und
somit auch die C Funktionen von file1.c aufrufen.
Das Schlüsselwort extern braucht man dann gar nicht. Das ist für seltene
Sonderfälle reserviert, wo man eine Funktion aufrufen muss, die nicht
in einer Header Datei deklariert wurde.
foobar schrieb:> extern int foo;> extern int bar(int);> #define BAZ 73
Das ist ja, was ich ciht genau verstehe. Warum sind foo und bar extern?
Dadurch, dass du in file2.c die file1.h einbindest, sind diese ja eh
bekannt
MArk schrieb:> Warum sind foo und bar extern?
Weil das hier quatsch ist. Mache es nicht so.
Alles, was in einer Header Datei steht ist implizit extern - man
schreibt es nicht extra hin.
Ich muss was korrigieren:
> Alles, was in einer Header Datei steht ist implizit extern
Alle Funktionen, die in einer Header Datei stehen sind implizit extern.
Bei Variablen müsste man es extra hinschreiben, aber Variablen in Header
Dateien sind wieder so ein Sonderfall, den Man meiden soll.
Stefanus F. schrieb:> Das ist für seltene Sonderfälle reserviert,
Nein. Das Schlüsselwort extern ist bei Funktionsdeklarationen
(Prototypen) implizit gegeben, es hinzuschreiben ist überflüssig, da
es nichts ändert:
1
voida(intx);
2
externvoidb(intx);
Hier ist beides identisch.
Anders sieht es bei Variablendefinitionen aus. Die können mit dem
Schlüsselwort extern zu einer Art Variablendeklaration umgewandelt
werden.
(in bla.h)
1
externintx;
Dann muss aber irgendo eine korrespondierende Definition vorhanden
sein:
(in bla.c)
Stefanus F. schrieb:> Alle Funktionen, die in einer Header Datei stehen sind implizit extern.
Nein. So geht das nicht. Das #include sorgt lediglich dafür, daß die
Datei, die dort genannt wird in die vorliegende Datei "hineinkopiert"
wird, eben so, als hätte man deren Inhalt per copy&paste selbst
hineinkopiert. Und das Ganze kriegt dann erst der Präprozessor. Da wird
garnix an deren Inhalt herumgemunkelt.
Und das extern vor externen Funktionen kann man sich ruhig angewöhnen.
Es widerspricht nicht den Regeln und es zieht gleich mit Variablen und
Konstanten, kurz allem, was man von der zugehörigen .c exportieren will
und was Speicherplatz kostet.
Ich mache das schon lange so, daß ich alles sowas in einer .h durchweg
mit extern davor hinschreibe. Daß es gerade bei Funktionen nicht
erforderlich ist, kann man als nebensächlich abtun. Wichtiger ist,
damit eine Besonderheit obsolet gemacht zu haben. Und für uns
menschliche Leser ist ein 'extern' auch gut, um es zu unterscheiden
von einem Prototypen, der in derselben Datei aufgelöst wird.
W.S.
Bitte beachten, dass W.S. einen gewöhnungsbedürftigen Programmierstil
hat und propagiert.
Wer unsicher ist, dem lege ich die Bücher von Kernighan und Ritchie
nahe. Die machen vor, wie es gedacht ist. Mit deren Stil kommen alle
Programmierer klar, ohne sich zu streiten.
> Das ist ja, was ich ciht genau verstehe. Warum sind foo und bar extern?> Dadurch, dass du in file2.c die file1.h einbindest, sind diese ja eh> bekannt
Ja, aber doch genau nur deswegen, weil sie in file1.h so deklariert
sind!
Da passiert nichts Magisches hinter dem Vorhang: der Compiler kennt nur
die eine c-Datei die er gerade übersetzt. Wenn er file2.c übersetzt,
schaut er sich file1.c nie an. Das einzige was passiert, ist, dass wenn
er auf "#include file1.h" trifft, diese Zeile durch den Inhalt von
file1.h ersetzt (reiner Textersatz). Alles was er über file1.c wissen
muß, muß da drin stehen, sonst gibt's Fehlermeldungen.
Ich habe auch den Verdacht, dass dir der Unterschied zwischen
Deklaration und Definition nicht klar ist - mal nach googlen. Dürfte
weiterhelfen.
>> Warum sind foo und bar extern?>> Weil das hier quatsch ist. Mache es nicht so.>> Alles, was in einer Header Datei steht ist implizit extern - man> schreibt es nicht extra hin.
Das ist Quatsch. Zum einen weiß der Compiler gar nicht, was eine
Header- und was eine C-Datei ist (manscht der Preprozessor vorher alles
zusammen). Und: bei Variablen muß man es schreiben. Bei
Funktionsprototypen darf man es weglassen. Ist ne Stil-Sache - bei
mir steht's immer dran: extern die Deklarationen, ohne extern resp mit
static die Definitionen.
foobar schrieb:> Das ist Quatsch.
Ich habe doch schon selbst korrigiert. Aber bitte, vielleicht möchten
noch weitere Leute nachtreten, macht ja so viel Spaß.
> Ich habe doch schon selbst korrigiert. Aber bitte, vielleicht möchten> noch weitere Leute nachtreten, macht ja so viel Spaß.
Ich versuche schon, vorm Senden auf Vorschau zu klicken und zu schauen,
ob das Geschriebene immer noch zum aktuellen Stand des Threads passt -
ab und zu vergesse ich's aber ...
Rufus Τ. F. schrieb:> Anders sieht es bei Variablendefinitionen aus. Die können mit dem> Schlüsselwort extern zu einer Art Variablendeklaration umgewandelt> werden.>> (in bla.h)extern int x;>> Dann muss aber irgendo eine korrespondierende Definition vorhanden> sein:>> (in bla.c)int x;
Was ich sonst so dazu im Netz gelesen habe suggeriert mir, der Compiler
muss alles in seiner Reichweite danach durchwühlen. Muss er das
wirklich?
Rufus Τ. F. schrieb:> Teo D. schrieb:>> der Compiler muss alles in seiner Reichweite danach>> durchwühlen.>> Was meinst Du damit?
Sorry, alle Foo_xy.c Files.
> Was ich sonst so dazu im Netz gelesen habe suggeriert mir, der Compiler> muss alles in seiner Reichweite danach durchwühlen. Muss er das> wirklich?
Muss er nicht und macht er auch nicht. Die einzige Stelle, wo er was
sucht, sind bei einem include-Statement: da sucht er die angegebene
Datei in den Include-Pfaden.
Der Compiler begnügt sich damit, den Typ jeder Variable und die Signatur
jeder Funktion zu kennen, also deren Deklaration.
Die Definition (Inhalt der Funktion, bzw. Speicherplatz der Variablen)
kann irgendwo anders sein.
Der Linker bringt das alles zusammen, und der meckert, wenn er die
Definition eines verwendeten Symbols (Variable/Funktion) nicht findet.
Der gcc enthält beide Komponenten (Compiler und Linker). Er wird
üblicherweise so aufgerufen, dass er zuerst jede *.c Datei einzeln
compiliert und dann zum Schluss alles zusammen linkt.
Also wenn ich das jetzt alles richtig verstanden habe,
dann schreibe ich ein extern vor der Deklaration der Funktion in
file1.h. Aber nicht weil es sein muss, sondern nur um sichtbar zu
machen,dass diese Funktion auch aus anderen Files (hier file2.c)
aufgerufen werden kann.
MArk schrieb:> dann schreibe ich ein extern vor der Deklaration der Funktion in> file1.h. Aber nicht weil es sein muss, sondern nur
... weil W.S. und "foobar" das für einen guten Stil halten.
Abgesehen von den beiden macht das niemand.
Daß die Funktion aus einer anderen Übersetzungseinheit heraus aufgerufen
werden kann, ist implizit dadurch gegeben, daß deren Deklaration in
einer Headerdatei aufgeführt wird.
Funktionen, von denen Du das nicht möchtest, bekommen keinen
Funktionsprototypen in einer Headerdatei, sondern einen
Funktionsprototypen am Beginn der sie enthaltenden C-Datei. Und um
Namenskonflikten aus dem Weg zu gehen, erhalten sie noch das
Schlüsselwort static verpasst, sowohl im Prototypen (der Deklaration)
als auch bei der Funktion selbst.
MArk schrieb:> Aber nicht weil es sein muss, sondern nur um sichtbar zu> machen,dass diese Funktion auch aus anderen Files (hier file2.c)> aufgerufen werden kann.
Nein, du hast es noch nicht verstanden.
Zunächst mal ist es dem Compiler egal, ob etwas in einer inkludierten
*.h Datei oder einen *.c Datei steht. Das wird alles zusammen gepappt
und in einem Rutsch als Einheit compiliert - so als wäre es eine Datei.
Wenn du eine Funktion ohne Rumpf hinschreibst:
1
voidsendMessage(chat*message);
Dann weiss der Compiler, was "sendMessage" ist. Von da an, kannst du die
Funktion sendMessage() benutzen. Dem Compiler ist vollkommen egal, ob
der Quelltext dieser Funktion in der aktuellen Einheit (*.c Datei +
inkludierte *.h Dateien) enthalten ist, oder nicht.
Der Linker, der die Einheiten zu einem lauffähigen Programm zusammen
fügt, erwartet in irgendeiner Einheit dies:
1
voidsendMessage(chat*message)
2
{
3
...
4
}
Wenn es fehlt, meckert er.
Man könnte "extern" vor die deklaration schreiben, um anzuzeigen, dass
die Definition dieser Funktion in einer anderen *.c Datei zu finden ist.
Nötig ist es nicht, weil das bei Funktions-deklarationen ohnehin
implizit bereits der Fall ist.
---
Anders ist es bei Variablen. Wenn du schreibst:
1
charname[5];
Dann wird hier in dieser Einheit festgelegt, dass Name eine Variable mit
5 Zeichen Größe ist. Hier wird quasi das RAM belegt. Wenn du hingegen
schreibst:
1
externcharname[5];
dann wird dem Compiler lediglich erklärt (deklariert), dass "name" eine
Variable mit 5 Zeichen ist, ohne Speicherplatz im RAM zu belegen.
Der Linker, der die einzelnen Einheiten deines Programms zusammenfügt
verlangt, dass in genau einer Einheit die Variable normal (ohne extern)
deklariert ist. Ansonsten existiert sie nicht im RAM und das Programm
wäre unvollständig.
Stefanus F. schrieb:> Bitte beachten, dass W.S. einen gewöhnungsbedürftigen Programmierstil> hat und propagiert.
Das hat gar nix mit gewöhnungsbedürftigen Programmierstil zu tun.
Das Funktionen im Gegensatz zu allem Anderen in C implizit als extern
deklariert werden, ist wieder mal so eine Unart oder besser Inkonsequenz
von C, die den Leuten und insbesondere Anfängern das Leben schwer macht.
Würde C konsquent an einem Stil festhalten wäre vieles deutlich
einfacher und der TO hätte diese Frage gar nicht gestellt/stellen
müssen.
Ich halte es deshalb für guten Programmierstil, wenn man gleichartige
Sachen auch gleichartig macht.
Diese impliziten Sachen sind zwar bequem, weil man weniger hinschreiben
muß, aber dem Verständnis sind sie eher abträglich.
Das betrifft jetzt nicht alleine C in anderen Sprachen ist das ähnlich.
In Fortran beispielsweise sind alle Variablen I, J, K, L, M und N
implizit integer alles andere ist real. Dennoch hat es sich
zwischenzeitlich durchgesetzt die Direktive "implicit none" anzugeben,
um genau dieses Verhalten abzuschalten und man gibt den Datentyp
explizit an.
W.S. schrieb:> Das #include sorgt lediglich dafür, daß die> Datei, die dort genannt wird in die vorliegende Datei "hineinkopiert"> wird, eben so, als hätte man deren Inhalt per copy&paste selbst> hineinkopiert. Und das Ganze kriegt dann erst der Präprozessor.
Das stimmt so auch nicht. Der Präprozessor ist nämlich derjenige, der
den Inhalt des Headers da reinkopiert. #include ist eine
Präprozessor-Direktive.
> Und das extern vor externen Funktionen kann man sich ruhig angewöhnen.
Man kann's aber auch lassen, wie der Rest der Welt.
> Es widerspricht nicht den Regeln und es zieht gleich mit Variablen und> Konstanten, kurz allem, was man von der zugehörigen .c exportieren will> und was Speicherplatz kostet.
Dann müsste man bei lokalen Variablen, die nicht statisch sind,
konsequenterweise auch auto davorschreiben.
Stefanus F. schrieb:> foobar schrieb:>> Das ist Quatsch.>> Ich habe doch schon selbst korrigiert. Aber bitte, vielleicht möchten> noch weitere Leute nachtreten, macht ja so viel Spaß.
Die Korrektur ist aber genauso falsch bzw unsinnig.
Dass das Schlüsselwort extern bei Funktionen entfallen darf, hat nichts
mit Headerdateien zutun.
Zeno schrieb:> Ich halte es deshalb für guten Programmierstil, wenn man gleichartige> Sachen auch gleichartig macht.
Eben. Wenn ein Funktions-extern keine Konsequenzen hat, sollte man es
auch nicht hinschreiben. Insbesondere, weil es Anfängern das Leben
schwermacht, denn es suggeriert, daß das einen Unterschied ausmachen
würde.
Ansonsten, wie willst Du denn in my_module.c die my_module.h
includieren, wenn in my_module.h die Funktionen als extern deklariert
sind? Das ergibt doch überhaupt keinen Sinn.
Oder soll man die Funktionen in der Headerdatei extern deklarieren, und
dann in der C-Datei nochmal ohne extern, und sich dann darum kümmern,
daß das auch miteinander immer in sync bleibt?
Stefanus F. schrieb:> Der Linker, der die Einheiten zu einem lauffähigen Programm zusammen> fügt, erwartet in irgendeiner Einheit dies:void sendMessage(chat*> message)> {> ...> }> Wenn es fehlt, meckert er.
Der Linker erwartet, das der Compiler ein Funktion erzeugt hat.
Und das tut der nur, wenn auch ein Funktionskörper da war.
Die Funktion kann auch von einem anderen Compiler einer anderen Sprache
sein.
Nop schrieb:> Oder soll man die Funktionen in der Headerdatei extern deklarieren, und> dann in der C-Datei nochmal ohne extern, und sich dann darum kümmern,> daß das auch miteinander immer in sync bleibt?
Ich verstehe den Absatz nicht.
Bei Variablen geht es doch auch mit extern. Und weil Deklaration und
Definition dort nicht immer unterschieden werden können, ist extern halt
Pflicht, bei Funktionen nicht.
A. S. schrieb:> Nop schrieb:>> Oder soll man die Funktionen in der Headerdatei extern deklarieren, und>> dann in der C-Datei nochmal ohne extern, und sich dann darum kümmern,>> daß das auch miteinander immer in sync bleibt?>> Ich verstehe den Absatz nicht.
Üblicherweise wird my_module.h nicht nur in anderen c-Dateien
includiert, sondern insbesondere auch in my_module.c, damit man
Vorwärtsdeklarationen hat. Das #include fügt aber nur den Text ein, also
hat man dann externe Funktionen deklariert, die in my_module.c aber gar
nicht extern sind. Das ergibt überhaupt keinen Sinn.
Nop schrieb:> A. S. schrieb:>> Nop schrieb:>>> Oder soll man die Funktionen in der Headerdatei extern deklarieren, und>>> dann in der C-Datei nochmal ohne extern, und sich dann darum kümmern,>>> daß das auch miteinander immer in sync bleibt?>>>> Ich verstehe den Absatz nicht.>> Üblicherweise wird my_module.h nicht nur in anderen c-Dateien> includiert, sondern insbesondere auch in my_module.c, damit man> Vorwärtsdeklarationen hat. Das #include fügt aber nur den Text ein, also> hat man dann externe Funktionen deklariert, die in my_module.c aber gar> nicht extern sind. Das ergibt überhaupt keinen Sinn.
Du hast das Wort extern missverstanden. Es bedeutet nicht das die
Funktion in einer anderen Quellcodedatei, Bibliothek, ... definiert ist,
sondern lediglich das es sich um eine Funktionsdeklaration handelt. Eine
Funktionsdeklaration sorgt lediglich dafür das der Compiler weiß das es
die entsprechende Fuktion (Name, Parameter und Rückgabewert) gibt. Da
dafür auch ein normaler Funktionsprototyp reicht (also Funktionskopf
ohne Körper), ist das Schlüsselwort "extern" an der Stelle optional. Ob
die Funktion dann letztlich in der gleichen Datei definiert ist die der
Compiler gerade abarbeitet, oder in einer anderen, ist egal.
> Üblicherweise wird my_module.h nicht nur in anderen c-Dateien> includiert, sondern insbesondere auch in my_module.c, [...]
Das ist sogar zu empfehlen (selbst wenn es in einem konkreten Fall nicht
nötig wäre), damit der Compiler überprüft kann, ob die Signatur des
Prototypes (aus dem .h) mit der tatsächlichen Funktion (im .c)
übereinstimmt und ggf nen Fehler schmeißt.
> [...] also hat man dann externe Funktionen deklariert, die in> my_module.c aber gar nicht extern sind.
Bei Variablen ist das doch immer so (mit extern im Header, ohne extern
im C-File). Da wird das extern benötigt, um die Deklaration von der
Definition zu unterscheiden. Bei Funktionen kann man das anhand der
Syntax entscheiden und das extern ist optional und default. (Ähnlich
wie das auto bei funktionslokalen Variablen oder das signed bei int.)
Einige Programmierer[1] benutzen es trotzdem genauso wie bei Variablen:
im Header extern, im C-File ohne - ist ne rein stilistische Sache. In
meinem Code signalisiert ein extern: hier kommt eine Deklaration, es
wird kein Speicher alloziert, etc. Deshalb käme ich auch nie auf die
Idee, bei einer Funktions*definition* ein extern zu schreiben.
[1] Und das sind nicht nur W.S. und ich - schaut euch z.B. mal die
Header im Linux-Kernel an - überall ein extern vor den Prototypen.
foobar schrieb:> - ist ne rein stilistische Sache
Ich schreibe kein extern davor. Aber mir käme es nie in den Sinn, dies
bei anderen, deren Code Klammerstyle Einrückungen Kommentarstil
chromacoding / IDE ich nicht kenne, abfällig zu kritisieren.(damit meine
ich nicht Dich!)
Was am Ende zählt ist die gesamt-lesbarkeit. Und in Grenzfällen ist das
auch von allem oben genannten abhängig. Und nein, das bedeutet nicht,
alles zu refakturieren bei neuer IDE.