Das C Seminar Walter Herglotz WJH-BOD 1 Funktionen und Adreßparameter Funktionen sind das A und O der Programmierung in „C“. Sie können unter vielen verschiedenen Aspekten betrachtet werden. Funktionen sind Behälter für Anweisungen. Sie sind die Grundlage für jede Teamarbeit, da sie getrennt übersetzt werden können. Mit ihnen wird die Wiederverwendung von Code möglich. Und sie sind auch Operatoren. Was ein „+“-Zeichen für „int“ oder „float“ ist, ein eingebauter Operator, das ist eine Funktion „addiere()“ für selbst definierte komplexe Zahlen. Für vordefinierte Datentypen gibt es auch vordefinierte Operatoren, für selbst definierte Datentypen schreiben wir eigene Funktionen. Funktionen Die oben angeführten Themen wollen wir nun nacheinander bearbeiten. Beginnen wir mit der Definition und dem Aufruf von Funktionen. Eine Funktion ist ein Programmstück mit eigenen Code und eigenen Daten sowie einer Schnittstelle. Der Begriff „Funktion“ ist wieder einmal aus der Mathematik entlehnt. Eine Funktion beschreibt die Abhängigkeit eines einzelnen Ausgangswertes von einem oder mehreren Eingangsparametern. Klassische Beispiele sind die Winkelfunktionen. Abhängig von einem Winkel zwischen 0 und 360 Grad wird ein Sinus- oder Cosinuswert berechnet. Der angegebene Winkelwert ist der Eingangsparameter, der Sinuswert das Ergebnis der Funktion. Funktionsdefinition Definieren wir zuerst eine Funktion, die aus der Menge der übergebenen Parametern den größten ermitteln und zurückgeben soll. Eine Funktion besteht aus einem Funktionskopf, der Schnittstelle und dem Funktionsblock. Funktionen 1 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 1 /* Funktionen (ANSI) */ 2 /* max.c */ 3 #include <stdio.h> 4 int maximum (int a, int b, int c) 5{ 6 int hilfe; 7 if (a > b) 8 hilfe = a; 9 else 10 hilfe = b; 11 if (hilfe > c) 12 return hilfe; 13 else 14 return c; 15 } 16 int main () 17 { 18 puts ("\n\x1b[2J"); 19 printf("\nMaximum: %d",maximum(10,33,12)); 20 return 0; 21 } Abbildung 1 Funktionsdefinition nach ANSI Die Schnittstelle der Funktion besteht immer aus drei Elementen: dem Namen der Funktion, den übergebenen Parametern und dem Rückgabewert. Der Name der Funktion hier ist „maximum“. Das Ergebnis ist vom Datentyp „int“. Und die drei übergebenen Parameter heißen a,b und c und sind alle vom Datentyp „int“. Bei der Definition einer Funktion passiert noch nichts. Die Funktion wird erst beim Aufruf aktiv. Sie wird entweder automatisch bei der schließenden Klammer („}“) verlassen oder explizit durch „return“ beendet. ANSI-C definiert die Parameter innerhalb der runden Klammern der Parameterliste. Im K&R-C war eine andere Schreibweise üblich. Unsere „maximum“-Funktion schaut in K&R-C wie folgt aus: Funktionen 2 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 1 /* Funktionen (K&R) */ 2 /* max1.c */ 3 #include <stdio.h> 4 int maximum (a, b, c) 5 int a,b,c; 6{ 7 int hilfe; 8 if (a > b) 9 hilfe = a; 10 else 11 hilfe = b; 12 if (hilfe > c) 13 return hilfe; 14 else 15 return c; 16 } 17 int main () 18 { 19 puts ("\n\x1b[2J"); 20 printf("\nMaximum: %d",maximum(10,33,12)); 21 return 0; 22 } Abbildung 2 Funktionsdefinition nach K&R In beiden Fällen haben wir vier lokale Variablen angelegt: die drei Parameter und die lokale Variable „hilfe“. Für den Programmierer bedeutet das, daß er in einer Funktion keine lokale Variable so nennen darf wie einen Parameter. Parameter und lokale Variablen leben nur während des Ablaufes der Funktion. Die beiden C-Dialekte unterscheiden sich nicht nur in der Schreibweise, sondern auch in der Bedeutung. Im K&R-C wurde die Parameterliste nie überprüft. Der Compiler achtete nicht darauf, ob die richtige Anzahl, die richtige Reihenfolge und die richtigen Datentypen übergeben wurden. Dieses Verhalten hat viele Programmierer veranlaßt, „C“ als unsichere Sprache oder einen gehobenen Assembler zu bezeichnen, zu Unrecht, wie ich meine. „C“ entstand ja unter UNIX. Und unter UNIX gibt es eine Spielregel: ein Programm erledigt eine einzige Aufgabe – und die gut. Der Compiler soll aus einem Quelltext Maschinencode erzeugen. Er sollte kein Listing ausdrucken und war auch nicht für die syntaktische Kontrolle zuständig. Um eine fehlerhafte Verwendung von Funktionen aufzuspüren gibt es ein komfortables Testprogramm, „lint“ („lint“ heißt Krümelchen), für den Ausdruck gibt es z. B. „pr“. Diese Denkweise war natürlich auch durch die geringe Leistung der Rechner vor 20 Jahren bedingt. Viele Programmierer haben auf den „lint“ verzichtet und dadurch so manches abenteuerliche Programm erstellt. Die heutigen Maschinen sind viel leistungsfähiger und damit können auch die Compiler größer werden. Die heutigen Compiler haben den Syntaxtest beim Übersetzen mit übernommen. Ergebnisrückgabe der Funktion Funktionen liefern normalerweise ein einzelnes Ergebnis. Dieses Ergebnis wird mit Hilfe des Schlüsselwortes „return“ zurückgegeben. Im Bild 9-2 wurde in den Zeilen 13 und 15 „return“ benutzt. Je nachdem, welcher Zweig erreicht wird, wird die eine oder andere „return“-Anweisung ausgeführt. „return“ hat zwei Aufgaben: die Funktion wird verlassen Funktionen 3 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 und falls ein Wert dahinter angegeben wurde, wird dieser Wert zwischen gespeichert und steht nach dem Verlassen im aufrufenden Programm zur Verfügung. Zumeist wird der Wert dann einer Variablen zugewiesen oder auch, wie wir es in der Hauptfunktion gemacht haben, gleich an eine andere Funktion weitergereicht. Sie werden häufig den Rückgabewert in Klammern gesetzt finden, so als wäre es ein Funktionsaufruf. Dies ist nicht notwendig, aber K&R haben öfters „return (wert);“ benutzt. Viele Programmierer haben sich dann daran gehalten. Prozeduren mit Rückgabe von „void“ Auch den Begriff der Prozedur kann man mit Hilfe des Ergebnistyps einführen. Es gibt die Möglichkeit ausdrücklich „nichts“ zurückzugeben. Und wenn man „nichts“ zurückgibt, darf man das nicht an eine Variable zuweisen. Das Schlüsselwort ist „void“ (ungültig). Wird eine Funktion definiert mit einem Rückgabetyp „void“, dann darf kein Ergebnis zurückgegeben werden. Hinter einem möglicherweise verwendeten „return“ steht kein Wert. Sollte die Prozedur aus Versehen in einer Zuweisung benutzt werden, dann kann der Compiler die fehlerhafte Verwendung erkennen. Betrachten wir als Beispiel eine Funktion, die in sehr vielen Programmen vorkommt. 1 /* usage: Bedienungshinweise */ 2 /* usage.c */ 3 #include <stdio.h> 4 #include <stdlib.h> 5 void usage (void) 6{ 7 printf("\nAufruf: XYZ Parameter\n"); 8 exit (1); 9} 10 Abbildung 3 Ausgabe der Kurzbedinungsanleitung „C“-Programmierer sind freundliche Zeitgenossen. Wenn man in einem Programm erkennt, daß der Bediener einen Fehler beim Aufruf gemacht hat, dann versucht man ihm dadurch zu helfen, daß man, falls möglich, eine Kurzbedienungsanleitung ausgibt und das Programm abbricht. Es wäre unüblich, den Benutzer zu beschimpfen, weil er einen Fehler gemacht hat. int status; .... status = ussage(); /* Führt zu einer Compiler-Meldung */ Abbildung 4 Fehlerhafter Prozedurauffruf Die Funktion, die diese Aufgabe erfüllt, heißt zumeist „usage()“. Sie soll als Prozedur verwendet werden und hat deshalb den Rückgabetyp „void“. „usage()“ gibt eine kurze Bedienungsanleitung aus und ruft die Funktion „exit()“ auf, die das Programm beendet. Funktionen 4 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 Für „exit()“ benötigen wir noch eine andere Informationsdatei, die „stdlib.h“. An Hand dieser Funktion kann man noch eine zweite Besonderheit zeigen; sie hat keinen Parameter. Um dem Compiler mitzuteilen, daß beim Aufruf kein Parameter benutzt werden darf, verwendet man das Wort „void“ in einem geringfügig anderen Sinn. Die Bedeutung hier ist: kein Parameter erlaubt. Bei ANSI-C soll man „void“ verwenden, um dem Compiler die Unterscheidung zwischen einer nicht geprüften Parameterschnittstelle nach K&R-C und einer leeren Parameterschnittstelle möglich zu machen. In K&R-C läßt man einfach die runden Klammern leer. K&R kennt „void“ nur als Datentyp. Funktionsaufruf Doch zurück zu unserem Beispiel mit der Funktion „maximum()“. Zuerst haben wir die Funktion definiert. Im Hauptprogramm wird sie aufgerufen. Beim Aufruf schreiben wir in die runden Klammern der Parameterliste die Werte, die die Funktion bearbeiten soll. Die Werte, die beim Aufruf verwenden werden, nennt man die aktuellen Parameter. Im Gegensatz dazu heißen die Parameter, die in der Funktiondefinition angegeben werden, formale Parameter. Zum Funktionsaufruf gehören drei Dinge, die der Compiler erledigen muß. Zuerst werden die formalen Parameter erzeugt und ihnen die Werte der aktuellen Parameter zugewiesen. Danach ruft man die Funktion auf. Sie kann nun auf die Parameter genauso zugreifen wie auf lokale Variablen. Sie können gelesen und auch verändert werden. Und am Schluß merkt man sich intern den Rückgabewert, verwirft die Parameter und lokalen Variablen und weist den Rückgabewert, falls gewünscht, einer Variablen zu. In unserem Beispiel haben wir den Rückgabewert von „maximum()“ gleich wieder als aktuellen Parameter für „printf()“ benutzt und das Ergebnis damit an „printf()“ weitergegeben. Der Rückgabewert muß nicht abgeholt werden. In vielen Fällen erzeugen Funktionen einen Rückgabewert, der nur in Sonderfällen beachtet wird. Hätten Sie gedacht, daß „printf()“ oder „puts()“ Funktionen sind, die einen Wert liefern? Prüfung der Schnittstelle In unserem Beispiel haben wir zuerst die Funktion definiert und sie danach aufgerufen. Das „zuerst“ und „danach“ bezieht sich auf die Leserichtung, die mit dem Bearbeitungsablauf des Compilers identisch ist. ANSI-C Compiler prüfen die richtige Verwendung. Dazu muß zum Zeitpunkt des Aufrufes die Schnittstelle bekannt sein. Drehen wir einfach einmal die Reihenfolge um. Schieben wir die „maximum()“-Funktion hinter „main()“. Beim Übersetzen wird uns ein ANSI-Compiler die Warnung ausgeben, daß beim Aufruf die Funktion unbekannt ist. Trifft der Compiler auf den Aufruf einer unbekannten Funktion, dann legt er automatisch eine Beschreibung dieser Funktion an. Er unterstellt, daß die Funktion mit dem Namen des Aufrufes existiert, eine ungeprüfte Parameterschnittstelle hat und als Ergebnis „int“ liefert. Dieses Verhalten war typisch für K&R-C. Um auch ältere Quellen übersetzen zu können, hat man diese Annahme auch in ANSI-C beibehalten. Der Standard sagt aber Funktionen 5 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 auch, daß bei einer Revision dieses Verhalten abgeschafft wird. Unsere Funktion „maximum()“ entspricht der Annahme des Compilers. Das Programm läuft daher immer noch einwandfrei. Experimentieren wir nun noch etwas. Lassen wir in beiden Fällen (ANSI und K&R) beim Aufruf einen Parameter weg. Im ANSI Fall kennt der Compiler die Schnittstelle und meldet den Fehler, daß beim Aufruf zu wenige Parameter angegeben wurden. Im K&R Fall prüft der Compiler die Parameterliste nicht und kann daher den Fehler nicht entdecken. Das Programm läuft oder auch nicht. Das Ergebnis hängt vom zufälligen Inhalt des Speichers ab. 1 /* Funktion mit autom. Deklaration */ 2 /* max2.c */ 3 #include <stdio.h> 4 int main () 5{ 6 puts ("\x1b[2J"); 7 printf("\nMaximum: %d",maximum(10,33,12)); 8 return 0; 9} 10 int maximum (int a, int b, int c) 11 { 12 int hilfe; 13 if (a > b) 14 hilfe = a; 15 else 16 hilfe = b; 17 if (hilfe > c) 18 return hilfe; 19 else 20 return c; 21 } 22 Abbildung 5 Auffruf einer noch unbekannten Funktion Funktionsdeklaration Wir können aber helfen, diesen Fehler trotzdem zu entdecken. Eine Funktion, die nicht in unserem eigenen Programm geschrieben wurde oder die erst später im Programm definiert werden wird, sollte man unbedingt deklarieren. Deklarieren heißt: „bekanntgeben“. Mit einer Deklaration wird kein Code erzeugt. Es ist nur eine Information für den Compiler, die er zur Überprüfung benötigt. Es gibt wieder zwei Arten von Deklarationen in „C“. Die ANSI-C Version haben wir im Bild 9-6 in der Zeile 4 benutzt. Die Deklaration besteht aus der Schnittstelle der Funktion. Es müssen alle Parametertypen angegeben werden. Der Programmierer kann zu Dokumentationszwecken hinter die Datentypen noch Parameternamen schreiben. Diese Namen werden vom Compiler entfernt. Er achtet nur auch die Art, die Anzahl und die Reihenfolge der Datentypen in einer Parameterliste. In K&R-C werden die Parameter nicht geprüft und daher auch nicht angegeben. Nur der Funktionen 6 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 Rückgabetyp und der Funktionsname sind hier wichtig. Die runden Klammern bleiben leer. Die Möglichkeiten nach K&R sind zur Zeit noch in den ANSI-Compilern implementiert. Normalerweise warnen sie aber, wenn man K&R-Konstrukte benutzt. Sie sollten die K&R-Schreibweise nach Möglichkeit nicht mehr benutzen. Da aber auch heute noch sehr viele Quellen mit alter Schreibweise existieren (und auch unsere UNIX-Testmaschine nur einen K&R-Compiler hat) müssen C-Programmierer für eine längere Übergangszeit beide Schreibweisen beherrschen. Definition oder Deklaration? Bei den Begriffen „Definition“ und „Deklaration“ gibt es gelegentlich Verwirrung. Im englischen Sprachgebrauch wird zwischen den beiden Begriffen oft nicht unterschieden. Dort spricht man zumeist nur von „declarations“. K&R machen hier eine löbliche Ausnahme. Eine Definition liegt vor, wenn die Bedeutung eines Namens festgelegt wird. Definieren Sie eine Variable oder eine Funktion, dann verbrauchen Sie auch Speicherplatz. 1 /* Funktionen */ 2 /* max3.c */ 3 #include <stdio.h> 4 int maximum (int, int, int); 5 int main () 6{ 7 puts ("\n\x1b[2J"); 8 printf("\nMaximum: %d",maximum(10,33,12)); 9 return 0; 10 } 11 int maximum (int a, int b, int c) 12 { 13 int hilfe; 14 if (a > b) 15 hilfe = a; 16 else 17 hilfe = b; 18 if (hilfe > c) 19 return hilfe; 20 else 21 return c; 22 } 23 Abbildung 6 Vorwärtsdeklaration einer Funktion Deklariert man einen Namen, dann gibt man nur die Bedeutung bekannt. Bei der Deklaration wird nie Speicherplatz verwendet. Die Deklaration dient nur als Prüfmöglichkeit für den Compiler. Informationsdateien / header files und Präprozessor Mit der Zeit werden die Funktionen, die wir schreiben oder benutzen, immer mehr und Funktionen 7 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 umfangreicher. Es wäre ziemlich aufwendig, wenn wir immer wieder alle Deklarationen abschreiben müßten. Die Lösung heißt Informationsdatei. In einer Informationsdatei werden alle Deklarationen zu einem bestimmten Bereich gesammelt. Jeder, der eine oder mehrere Funktionen aus dem Bereich benutzen will, gibt dem Compiler die Anweisung, die gewünschte Datei einzulesen. Wir haben bisher oft die Datei „stdio.h“ eingelesen, die die Deklarationen für die Standard-Ein-/Ausgabe enthält. Für das Einlesen solcher Dateien und für weitere Dienstleistungen besitzt der Compiler einen sogenannten Präprozessor, dem ein eigenes Kapitel gewidmet ist. Die Aufgabe des Präprozessors ist es, Textersetzungen durchzuführen. Er ist im Grunde ein simpler Editor. Die Anweisungen an ihn beginnen mit einem ‘#’. Für ältere Compiler gilt, daß das ‘#’ in der ersten Spalte stehen muß. Die „#include“-Anweisung veranlaßt den Präprozessor, das Einlesen zu unterbrechen und erst die angegebene Datei zu holen. Erst wenn die gewünschte Datei komplett eingelesen und an den Compiler weitergereicht wurde, wird mit der Bearbeitung der ursprünglichen Datei fortgefahren. Es gibt zwei Bereiche, in denen der Präprozessor nach Dateien suchen kann. Geben Sie die gewünschte Datei in spitzen Klammern an, dann bedeutet das, daß die Datei beim Compiler gesucht werden soll. Bei unserem DOS-Compiler wäre das der Pfad „\tc\include“; bei UNIX ist der Pfad normalerweise „/usr/include“. Schreibt man den Dateinamen in Anführungszeichen, dann wird die Datei beim Anwender, also im momentanen Verzeichnis gesucht. Die Idee dabei ist, daß man für jedes Projekt ein eigenes Verzeichnis anlegt und darin die zugehörigen Dateien speichert. Parameterübergaben Bei den meisten Aufrufen von Funktionen geben wir Parameter mit. Es gibt verschiedene Arten der Übergabe (wie wird übergeben?) und es gibt verschiedene Typen, die übergeben werden können (was wird übergeben?). Die Art der Übergabe ist in „C“ einfach. Es gibt nur eine, die Wertübergabe (call by value). Die WertübergabeWertübergabe ist eine Zuweisung des aktuellen Parameters an einen formalen Parameter. Beim Aufruf wird jeder aktuelle (Aufruf-) Parameter in den zugehörigen formalen Parameter kopiert, der bei der Definition der Funktion verwendet wurde. Dieser Kopiervorgang ist notwendig, da formale Parameter lokale Variable der aufgerufenen Funktion sind. Eine Veränderung der Parameter in einer Funktion hat damit keinerlei Auswirkungen auf die möglicherweise übergebenen Variablen der aufrufenden Funktion. Dieses Verfahren ist in vielen Fällen sehr wünschenswert. Eine Funktion muß keinerlei Rücksicht auf unerwünschte Seiteneffekte nehmen, da die Parameter nur ihr gehören. Schwierig wird es, wenn wir sehr große Variablen übergeben wollen. Große Variablen können Felder oder sogenannte Strukturen (in Pascal: records) sein, da dann mit dieser Methode die Kopierzeiten unerwünscht lang werden können. Ein anderer Problemfall Funktionen 8 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 entsteht, wenn eine Funktion einmal die Originalvariablen des aufrufenden Programmes verändern können soll. Stellen Sie sich dazu die Situation vor, daß ein Programm einen Puffer besitzt, der nun durch Einlesen von einer Datei oder von der Tastatur gefüllt werden soll. Dann muß das aufrufende Programm der Lesefunktion sagen können, wohin es die Daten schreiben soll. Adressen und Adreßübergabe „C“ löst diese Probleme mit Hilfe von Adressen und Adreßvariablen. 1 /* Adressuebergabe */ 2 /* scanf1.c */ 3 #include <stdio.h> 4 int main () 5{ 6 float fvar; 7 puts ("\x1b[2J"); 8 puts ("Tippen Sie eine float-Zahl:"); 9 scanf ("%f",&fvar); 10 printf("\nVariable: %f",fvar); 11 return 0; 12 } 13 Abbildung 7 Übergabe einer Adresse an "scanf()" Im Beispiel 9-7 wird die Funktion „scanf()“ verwendet. Sie ist das Gegenstück zu „printf()“. Ihr Name bedeutet „formatiertes Einlesen“. Das gewünschte Format wird als Textkonstante im ersten Parameter angegeben. Das „%f“ bedeutet „Fließkommawert“. Der zweite Parameter ist die Zielvariable, in der der eingelesene Wert abgelegt werden soll. Vor dem Variablennamen steht der Adreßoperator „&““. An die Funktion „scanf()“ wird daher die Adresse von „fvar“ übergeben. Nun weiß „scanf()“, wo das Ergebnis abgelegt werden soll. „scanf()“ wird auf die Eingabe eines Textes von der Tastatur warten, der als Fließkomma-Zahl interpretiert werden kann. Er muß daher einen Punkt oder einen Exponenten beinhalten. Nachdem der Text eingelesen wurde, wandelt „scanf()“ den Text in die interne Darstellung als „float“-Zahl und legt das Ergebnis an dem Ort ab, der ihr als zweiter Parameter angegeben wurde. In „C“ werden automatisch alle Felder mit Hilfe einer Adresse übergeben, um das Kopieren der Variablen zu vermeiden. Eine Adresse läßt sich schneller an eine Funktion übergeben als große Daten. Wir haben bei den Feldern schon erwähnt, daß der Name des Feldes für die konstante Startadresse steht. Innerhalb einer Funktion greift man mit Hilfe der übergebenen Adresse auf den Speicherplatz zu. Um die Adresse übergeben zu können, brauchen wir in der Funktion einen Parameter, der in der Lage ist, eine Adresse aufzunehmen. Variablen, deren Inhalt Adressen sind, nennen wir Zeiger (engl. pointer). „C“ Funktionen 9 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 unterscheidet sich von anderen Hoch-Sprachen dadurch, daß „C“ dem Programmierer den Umgang mit Adressen erlaubt und sogar eine komplette Adreßrechnung bereitstellt. Doch zuerst zu einem kleinen Beispiel. Als Parameter der Funktion „indirekt()“ definieren wir eine Variable mit dem Datentyp „Zeiger auf float“. Die Definition liest sich daher als „float-zeiger fzeiger“. Anders ausgedrückt, ist „fzeiger“ eine Variable, die Adressen von „float“-Variablen aufnehmen darf. Andere Adressen dürfen in dieser Variablen nicht gespeichert werden. Man sagt auch, „C“ kennt den Begriff der typisierten Zeiger. 1 /* Indirekte Adressierung */ 2 /* zeiger1.c */ 3 #include <stdio.h> 4 void indirekt (float * fzeiger) 5{ 6 *fzeiger = 3.14159; 7} 8 int main () 9{ 10 float fvar; 11 puts("\x1b[2J"); 12 indirekt (&fvar); 13 printf ("\nErgebnis: %f",fvar); 14 return 0; 15 } 16 Abbildung 8 Indirekter Zugriff innerhalb einer Funktion Schauen wir uns im Beispiel zuerst einmal die „main()“-Funktion an. Dort definieren wir eine „float“-Variable, deren Inhalt unbestimmt ist. Sie wurde nicht initialisiert. Nach dem Löschen des Bildschirms rufen wir die Funktion „indirekt()“ und übergeben beim Aufruf die Adresse der Variablen „fvar“. Dabei wird an den formalen Parameter „fzeiger“ diese Adresse zugewiesen. In der Funktion greifen wir mit dem „*“ in der Zeile 6 indirekt zu. Die Zeile kann man auch so lesen: schreibe den Wert 3.14159 unter der Adresse in den Speicher, die man in der Variablen „fzeiger“ findet. Ab der Adresse stehen genügend Bytes für eine „float“-Variable zur Verfügung. Das Bild 9-9 soll das noch einmal veranschaulichen. Funktionen 10 24.02.02 Das C Seminar Walter Herglotz WJH-BOD 1 Im obigen Bild haben wir zwei Variablen angelegt. Die „float“-Variable benötigt vier Bytes. Der Zeiger soll ein reiner „offset“-Zeiger sein, wie er in den sogenannten „small“-Compilermodellen verwendet wird. (Wegen der Segmentierung der 80X86-Prozessoren kann man trotz des größeren Speicherraumes mit 16-Bit-Zeigern auskommen, sofern man Daten und Code auf je 64k begrenzt.) Den Zeiger laden wir mit der Adresse der „float“-Variablen. Damit zeigt der Zeiger auf die „float“-Variable. Dieses Vorbesetzen geschah im Programm bei Abbildung 9 Speicheraufbau mit Zeiger und der Übergabe der Adresse an den Variable formalen Parameter. Mit diesem ersten Einstieg in die Zeigerthematik wollen wir dieses Kapitel beschließen. Im nächsten Kapitel Am Beginn des Kapitels haben wir erwähnt, daß Funktionen dadurch zur Teamarbeit beitragen, daß sie getrennt übersetzt werden können. Die getrennt übersetzten Teile fügt der Linker zu einem Programm zusammen. Wir werden uns daher mit der Modularisierung beschäftigen. Funktionen 11 24.02.02