|
|
Funktionen auslagern (C)Dieser Artikel basiert auf folgendem Post: http://www.mikrocontroller.net/forum/read-1-259088.html#259151 Autor: Karl Heinz Buchegger (Heinzi) Datum: 11.11.2005 18:48 [Bearbeiten] Funktionen/Prototypen in Header Files verteilenDen Prototypen deklarierst Du im Headerfile. Und dieses Headerfile #include -st Du dann dort wo du's brauchst, zb. im main(). Das Grundprinzip ist doch folgendes in C: Jedes Source Code File wird für sich alleine vom Compiler übersetzt. Und in jeder dieser Source Code Einheit muss alles deklariert sein, was der Compiler nicht von sich aus kennt. Und zwar bevor man es verwendet. Dazu gehören zb alle Funktionen aber auch globale Variablen. Natürlich kannst Du auch komplett auf den Header pfeifen und in jedem Source Code die Dinge einzeln deklarieren. Etwa so: File1.c
File2.c
Main.c
In jedem C-File sind alle Funktionen als Prototyp aufgeführt, bevor die Funktione dann verwendet (aufgerufen) wird. Dadurch kennt der Compiler die Funktion und weiß, dass es sich beim Funktionsnamen um keinen Tippfehler handelt und er weiß auch welche Argument die jeweilige Funktion benutzt und welchen Return Wert sie hat. Aber Du merkst schon, wo das hinführt. Wenn Du foo() veränderst, zb. indem ein zusätzlicher Parameter mit aufgenommen wird, so musst Du die Änderung in diesem Fall an 4 Stellen machen:
Das das bei vielen Funktionen und vielen Files mehr als fehleranfällig ist, brauchen wir wohl nicht zu diskutieren. Dazu gibt es aber in C den Mechanismus des #include Bevor sich der Compiler den eigentlichen Programtext vornimmt, rauscht der erstmal durch den Preprozessor. Der ersetzt alle Zeilen mit
durch den aktuellen Inhalt der Datei namens 'Dateiname'. Du schreibst also zb. Foo.h
File1.c
File2.c
Übersetzt du zb. File1.c, dann passiert Folgendes: Zuerst nimmt sich der Preprozessor den Text vor und ersetzt die Zeile
mit dem Inhalt der Datei 'Foo.h'. Als Ergebnis erhält er:
und erst dieses Ergebnis wird durch den tatsächlichen C Compiler gejagt. Der ist natürlich happy damit, denn in der Funktion foo2() wird eine andere Funktion foo() benutzt und deren Protoyp wurde ja korrekt angegeben. Korrekt bedeutet: Bevor die Funktion verwendet wird (aufgerufen wird), steht in der Source Code Datei der Prototyp. Der Prorotyp teilt dem Compiler mit, daß es diese Funktion tatsächlich gibt, welche Argumente sie erwartet und welchen Rückgabetyp sie liefert. Diese Information benötigt der Compiler um zu überprüfen, ob
Aber: Ändert sich an der Funktion foo() etwas, dann änderst Du einfach nur an einer einzigen Stelle den Protoypen: in Foo.h. Alle anderen, File1.c, File2.c und Main.c kriegen das automatisch mit, da sie ja Foo.h inkludieren und somit die Änderung beim nächsten kompilieren indirekt über Foo.h einfliessen. [Bearbeiten] Variablen/Lookup TabellenIm Prinzip machst Du genau das gleiche, nur kommt Dir hier die ODR (One Definition Rule) in die Quere. Jedes Teil darf nur einmal definiert werden! Bei einem Protoypen weiss der Compiler auch so, dass es sich um einen Prototypen handelt. Etwas in der Form
kann nur ein Protoyp (also eine Deklaration) sein. Eine Definition wuerde ganz anders aussehen
bei globalen Variablen ist das aber anders. Ist
nun eine Definition oder eine Deklaration? Antwort: Es ist eine Definition. Wenn Du sowas in Foo.h stehen hättest, dann würde der Compiler klarerweise beim übersetzen von File1.c eine Variable namens Global1 anlegen. Dasselbe würde er aber auch machen, wenn File2.c und Main.c übersetzt werden. Das geht aber nicht, denn dann würde der Linker 3 Variablen namens Global1 sehen und nicht wissen, welche denn die Richtige ist. Also muss man das anders machen: Die Definition muss zu einer Deklaration werden: Foo1.h
Das extern bewirkt, dass für Global1 jetzt keine Variable mehr erzeugt wird. Es ist nur noch die Information, daß es irgendwo eine Variable namens Global1 gibt und das diese vom Typ int ist. Na zb. hier: Main.c
Damit existiert die Variable Global1 tatsächlich und alle anderen Verweise in den anderen *.c Dateien haben somit ein tatsächliches Ziel. [Bearbeiten] Definition und DeklarationWas ist also der Unterschied zwischen einer Definition und einer Deklaration? Bei einer Definition wird im fertigen Programm vom Compiler tatsächlich etwas erzeugt. Eine Definition mündet also darin, dass im fertigen Programm Speicher für etwas reserviert wird. Eine Definition ist zb. eine Funktionsdefinition oder eine Variablendefinition. Bei einer Deklaration teilt man dem Compiler lediglich mit, dass es etwas gibt, wie es heißt und welche Eigenschaften es hat. Der Compiler speichert dieses Wissen in Compilerinternen Tabellen und benutzt es, wenn er ein Programm übersetzt. Eine Definition ist immer gleichzeitig auch eine Deklaration. Denn durch die Definition wird dem Compiler ja auch mitgeteilt, dass etwas existiert, wie es heißt und welche Eigenschaften es hat. Aber im Falle einer Funktion enthält die Definition zusätzlich noch den tatsächlichen Funktionsinhalt; bzw. im Falle einer Variablen enthält die Definition implizit noch die Anweisung: Diese Variable müsste jetzt erzeugt werden. |