Funktionen und Parameter

Werbung
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
Herunterladen