Forum: PC-Programmierung externe funktion in c


von MArk (Gast)


Lesenswert?

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

von foobar (Gast)


Lesenswert?

> 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.

von MArk (Gast)


Lesenswert?

foobar schrieb:
> Das ist die übliche Methode.

perfekt, danke

von MArk (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

file1.h:
1
extern int foo;
2
extern int bar(int);
3
#define BAZ 73
file1.c:
1
#include "file1.h"
2
3
int foo;
4
int bar(int x)
5
{
6
    return x*42; 
7
}
file2.c:
1
#include "file1.h"
2
...
3
   printf("%d\n", foo+BAZ, bar(42));
4
...

von MArk (Gast)


Lesenswert?

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

von Stefan F. (Gast)


Lesenswert?

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.

von Dumdi D. (dumdidum)


Lesenswert?

MArk schrieb:
> Warum sind foo und bar extern?

Kann man auvh weglassen. Ist bei Funktionen der default.

von Stefan F. (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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
void a(int x);
2
extern void b(int x);

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
extern int x;

Dann muss aber irgendo eine korrespondierende Definition vorhanden 
sein:

(in bla.c)
1
int x;

von W.S. (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

> 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.

von Stefan F. (Gast)


Lesenswert?

Die Bücher von Bjarne Stroustrup kann ich auch empfehlen. Das ist ein 
alter Hase, der weiß was er tut.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Stefanus F. schrieb:
> Die Bücher von Bjarne Stroustrup kann ich auch empfehlen.

Es geht um C, nicht um C++.

von foobar (Gast)


Lesenswert?

>> 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.

von Stefan F. (Gast)


Lesenswert?

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ß.

von foobar (Gast)


Lesenswert?

> 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 ...

von Teo D. (teoderix)


Lesenswert?

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?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Teo D. schrieb:
> der Compiler muss alles in seiner Reichweite danach
> durchwühlen.

Was meinst Du damit?

von Teo D. (teoderix)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

> 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.

von Stefan F. (Gast)


Lesenswert?

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.

von Teo D. (teoderix)


Lesenswert?

Stefanus F. schrieb:
> Der Linker

Autsch, das tut wehhhh. Den hatte ich ganz vergessen. :}
Danke

von MArk (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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
void sendMessage(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
void sendMessage(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
char name[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
extern char name[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.

von Zeno (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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?

von Stefan F. (Gast)


Lesenswert?

Wer Werbung im Briefkasten erlaubt müsste dann auch ein Schild "Bitte 
Werbung einwerfen" aufkleben.

von Dirk B. (dirkb2)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Thomas B. (sinotech)


Lesenswert?

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.

: Bearbeitet durch User
von foobar (Gast)


Lesenswert?

> Ü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.

von A. S. (Gast)


Lesenswert?

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.

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.