9. Funktionen
  Bisher waren die Programme noch recht klein. Wird die zu lösende Aufgabe komplexer, so
  sollte das Programm in übersichtliche Teile zerlegt werden. Diese Teile können
  dann immer noch so komplex sein, dass sie weiter unterteilt werden usw. Der Entwurf
  dieser Hierarchie nennt sich Prinzip der schrittweisen Verfeinerung.
  Diese Teilprogramme werden Funktionen genannt. Eine Funktion erledigt
  eine abgeschlossene Teilaufgabe. Weiterhin müssen der Funktion die zu verarbeitenden
  Werte mitgegeben werden. Dieses geschieht mit sogenannten Argumenten
  bzw. Parametern. Die Funktion liefert das Ergebnis an die aufrufende
  Funktion zurück. Dieses Ergebnis wird auch Rückgabewert
  genannt. Ein Beispiel ist die Sinus-Funktion y = sin(x): Hier wird der
  Wert x als Parameter der Funktion übergeben und das Ergebnis der Sinus-Funktion wird
  der Variablen y zugewiesen.
  Eine Funktion muss nur einmal definiert werden. Danach kann sie beliebig oft durch
  Nennung ihres Namens (dem Funktionsnamen) aufgerufen werden. Eine
  Funktion ist also wiederverwendbar!
  
9.1. Funktionsprototypen und -definitionen
  Bei Funktionen wird zwischen der Deklaration (Bekanntmachung des Namens) und der
  Definition (Belegung eines Speicherbereichs) unterschieden. Die Deklaration der Funktionen
  wird im allgemeinen vor dem Hauptprogramm main() geschrieben. Diese
  Deklarationen werden hier Funktionsprototypen genannt. Die Syntax
  eines Funktionsprototyps sieht folgendermaßen aus:
  Rückgabe-Datentyp Funktionsname(Datentyp [Parametername], ...);
  
  Die Parameter werden bei Funktionsprototypen und -definitionen auch
  Formalparameter genannt. Die Parameteranzahl kann bei jeder Funktion
  unterschiedlich sein. Es können Funktionen mit einer Parameteranzahl zwischen 0 und
  "beliebig viele" deklariert und definiert werden. Beim Aufruf der Funktion dagegen
  muss exakt die bei der Deklaration bzw. der Definition vorgegebene Anzahl der Parameter
  angegeben werden. Der Name des Parameters muss bei der Deklaration nicht angegeben werden,
  wohl aber bei der Definition.
  Durch die Deklaration "weiß" der Compiler, dass irgendwo eine Funktion mit dem
  angegebenen Funktionsnamen definiert ist, wieviele Parameter sie hat und welchen Datentyp
  der Rückgabewert hat. Wird diese Funktion im Programm aufgerufen, kann der Compiler
  jetzt eine Syntaxprüfung machen, ohne dass die Funktion definiert sein muss.
  Erst durch die Funktionsdefinition kann die Funktion auch angewendet
  werden, denn erst hier wird der Quellcode angegeben und wird entsprechend Speicherplatz
  dafür reserviert. Die Syntax einer Funktionsdefinition sieht folgendermaßen
  aus:
  
  Rückgabe-Datentyp Funktionsname(Datentyp Parametername, ...)
  {  Anweisungen;
  }
  Der wichtigste Unterschied in der Syntax zwischen Funktionsprototyp und -definition ist,
  dass beim Prototyp am Ende der Zeile ein Semikolon steht, während bei der
  Definition kein Semikolon stehen darf. Dafür folgt bei der Definition in den
  darauffolgenden Zeilen der Quellcode der Funktion, der sogenannte
  Funktionskörper. Der Quellcode der Funktion wird - auch wenn die
  Funktion nur eine Anweisung beinhaltet - zwischen einem Paar geschweifte Klammern gesetzt
  (genauso wie bei der main-Funktion).
  Beispiel:
   kap09_01.c
  kap09_01.c
  
  01 #include <stdio.h>
  02 
  03 /* Funktionsprototyp: */
  04 double Average(double, double, double);
  05 
  06 /* Hauptprogramm: */
  07 int main()
  08 {
  09    double a = 4.5, b = 3.1415, c = 7.99;
  10 
  11    /* Aufruf der Funktion in der printf-Anweisung: */
  12    printf("Durchschnitt: %f\n", Average(a, b, c));
  13 
  14    return 0;
  15 }
  16 
  17 /* Funktionsdefinition: */
  18 double Average(double Zahl1, double Zahl2, double Zahl3)
  19 {
  20    return (Zahl1 + Zahl2 + Zahl3) / 3.0;
  21 }
  
  Aufgerufen wird die Funktion durch den Funktionsnamen gefolgt von den Parametern, die in
  Klammern gesetzt werden. Die Parameter werden in diesem Fall Aktualparameter
  genannt. Die Klammern müssen auch dann gesetzt werden, wenn keine Parameter der
  Funktion übergeben werden. Die Syntax für einen Funktionsaufruf lautet also
  Funktionsname(Variablenname bzw. Konstante, ...);
  Der Rückgabewert der Funktion kann in einer Variablen gespeichert werden oder wie im
  Beispiel gleich einer anderen Funktion übergeben werden (printf
  ist auch eine Funktion) oder einfach "vergessen" werden, wenn das Funktionsergebnis nicht
  benötigt wird. Z.B. liefert auch die printf-Funktion ein Ergebnis
  zurück (nämlich die Anzahl der ausgegebenen Zeichen), aber dieses Ergebnis wird
  nicht weiter benötigt, also wird es auch nicht weiter verarbeitet.
  
9.2. Gültigkeitsbereiche und Sichtbarkeit
  Grundsätzlich sind alle deklarierten Namen von Variablen, Typen, Konstanten,
  Funktionen, usw. nach der Deklaration nur innerhalb des Blocks gültig, in dem sie
  deklariert wurden. D.h. z.B. alle Variablen, die innerhalb einer Funktion deklariert bzw.
  definiert werden, sind nur innerhalb dieser Funktion bekannt und sichtbar; in jeder anderen
  Funktion sind diese Variablen unbekannt. Diese Variablen werden lokale
  Variablen genannt; sie sind nur in der lokalen Umgebung (innerhalb des Blocks) bekannt.
  Nach Verlassen des Blocks werden sie wieder vernichtet, d.h. ihr Speicherplatz wird wieder
  freigegeben.
  Variablen, die außerhalb von allen Blöcken deklariert werden, sind innerhalb der
  ganzen Datei gültig, d.h. innerhalb der main- und innerhalb aller
  anderen Funktionen, die in der gleichen Quellcodedatei definiert sind. Dadurch, dass
  diese Variablen global gelten, werden sie auch globale Variablen
  genannt.
  Grundsätzlich sollte auf globale Variablen nach Möglichkeit verzichtet werden und
  statt dessen lieber diese Variablen lokal angelegt und bei Bedarf als Parameter an die
  Funktionen übergeben werden.
  Im folgenden Beispielprogramm gibt der Compiler jeweils eine Fehlermeldung für die Zeilen
  13 und 24 aus, da in beiden Fällen die Variablen nicht bekannt (deklariert) sind.
  Beispiel:
   kap09_02.c
  kap09_02.c
  
  01 #include <stdio.h>
  02 
  03 int a;           /* globale Variable */
  04 
  05 void Test(void); /* keine Parameter, kein Rückgabewert */
  06 
  07 int main()
  08 {
  09    int b;        /* lokal im Hauptprogramm */
  10 
  11    a = 0;        /* erlaubt, da a global */
  12    b = 0;        /* erlaubt, da b in diesem Block deklariert */
  13    c = 0;        /* FEHLER!, da c nur in Funktion bekannt */
  14    Test();
  15 
  16    return 0;
  17 }
  18 
  19 void Test(void)
  20 {
  21    int c;        /* lokal in dieser Funktion */
  22 
  23    a = 0;        /* erlaubt, da a global */
  24    b = 0;        /* FEHLER!, da b nur im Hauptprogramm bekannt */
  25    c = 0;        /* erlaubt, da c in dieser Funktion deklariert */
  26 }
  
  Nach diesen Angaben ist es nun auch möglich, mitten im Programm einen Block
  einzusetzen und in diesem Block lokale Variablen zu deklarieren bzw. zu definieren. Das
  sieht dann wie folgt aus.
  Beispiel:
   kap09_03.c
  kap09_03.c
  
  01 #include <stdio.h>
  02 
  03 int main()
  04 {
  05    int a;    /* lokal im Hauptprogramm */
  06 
  07    a = 0;    /* erlaubt, da a im Hauptprogramm deklariert */
  08 
  09    /* Block innerhalb des Hauptprogramms: */
  10    {
  11       int b; /* lokal in diesem Block */
  12 
  13       a = 3; /* erlaubt */
  14       b = 3; /* erlaubt */
  15    }
  16    b = 0;    /* FEHLER!, da b nur in dem Block bekannt */
  17 
  18    return 0;
  19 }
  
  Eine Ausnahme bilden lokale Variable, die innerhalb eines Blocks oder einer Funktion als
  statische Variable definiert werden. Dazu wird vor dem Datentyp
  zusätzlich das Schlüsselwort static verwendet. Diese
  statischen Variablen werden nach Verlassen des Blocks oder der Funktion nicht vernichtet
  und haben bei erneutem Aufruf des Blocks oder der Funktion noch den alten Wert. Ferner wird
  eine evtl. Variableninitialisierung nur bei der erstmaligen Definition ausgeführt; bei
  allen weiteren Aufrufen wird die Initialisierung ignoriert.
  Beispiel:
   kap09_04.c
  kap09_04.c
  
  01 #include <stdio.h>
  02 
  03 void Test(void);
  04 
  05 int main()
  06 {
  07    int i;
  08 
  09    for (i = 0; i < 3; i++)
  10       Test();
  11 
  12    return 0;
  13 }
  14 
  15 void Test(void)
  16 {
  17    static int Anzahl = 0;
  18    /* wird nur beim 1. Aufruf auf Null gesetzt! */
  19 
  20    Anzahl++;
  21    printf("Anzahl = %i\n", Anzahl);
  22 }
  
  Die Ausgabe des Programms ist
   Ausgabe = 1
 Ausgabe = 2
 Ausgabe = 3
  Ohne das Schlüsselwort static würde dreimal eine 1
  ausgegeben werden, da die lokale Variable bei jedem Funktionsaufruf wieder neu erzeugt
  würde.
  Die Parameter einer Funktion werden innerhalb der Funktion als lokale Variablen behandelt;
  von außen betrachtet stellen sie die Daten-Schnittstelle zur Funktion dar. Mehr dazu
  im nächsten Abschnitt.
  
9.3. Funktionsschnittstelle
  Der Datentransfer in Funktionen hinein (Parameter) und aus Funktionen heraus
  (Rückgabewert) wird durch die Beschreibung der Schnittstelle
  festgelegt. Unter einer Schnittstelle ist eine formale Vereinbarung zwischen Aufrufer und
  Funktion über die Art und Weise des Datentransports zu verstehen. Auch das, was die
  Funktion leistet, gehört zur Schnittstelle (sollte als Kommentar beim Funktionskopf
  stehen). In diesem Abschnitt soll es nur um den Datentransfer gehen. Die Schnittstelle ist
  durch den Funktionsprototyp eindeutig beschrieben und enthält folgendes:
   den Rückgabetyp der Funktion,
   den Funktionsnamen,
   Parameter, die der Funktion bekannt gemacht werden inkl. deren Datentypen und
   die Art der Parameterübergabe.
  Der Compiler prüft, ob die Definition der Schnittstelle bei einem Funktionsaufruf
  eingehalten wird.
  Für den Datentransfer in die Funktion hinein gibt es zwei verschiedene Arten des
  Datentransports: Die Übergabe per Wert und die Übergabe per Zeiger.
  Übergabe per Wert
  Bei der Übergabe per Wert wird der Wert in den sogenannten Stack
  (einem Zwischenspeicher u.a. für die Parameterübergabe) kopiert - daher wird dies
  manchmal auch Übergabe per Kopie genannt. Innerhalb der Funktion werden die Werte
  auf dem Stack als lokalen Variable verwendet, die am Ende der Funktion wieder vernichtet werden,
  während die Originale unverändert bleiben.
  Beispiel:
   kap09_05.c
  kap09_05.c
  
  01 #include <stdio.h>
  02 
  03 int Addiere_3(int);
  04 
  05 int main()
  06 {
  07    int Ergebnis, Zahl = 2;
  08 
  09    printf("Wert von Zahl = %i\n", Zahl);
  10    Ergebnis = Addiere_3(Zahl);
  11    printf("Ergebnis 'Addiere_3(Zahl)' = %i\n", Ergebnis);
  12    printf("Wert von Zahl = %i (unveraendert)\n", Zahl);
  13 
  14    return 0;
  15 }
  16 
  17 int Addiere_3(int x)
  18 {
  19    x += 3;
  20    return x;
  21 }
  
  Die Übergabe per Wert sollte generell verwendet werden, wenn ein Objekt von der
  Funktion nicht verändert werden soll. Wenn der Kopiervorgang in den Stack allerdings
  zu lange dauert (bei sehr großen Objekten oder bei häufigem Aufruf der
  Funktion), kann auch eine Übergabe per Zeiger geschehen. Dann sollte allerdings
  sichergestellt werden, dass die Funktion den übergebenen Wert nicht
  verändert, z.B. durch Verwendung von const-Parametern.
  Übergabe per Zeiger
  Die Übergabe per Zeiger ist ein Spezialfall der Übergabe per Wert, es wird
  nämlich der Zeiger auf das Objekt im Stack abgelegt. Innerhalb der Funktion wird
  dieser Zeiger auf dem Stack wieder als lokale Variable behandelt. Der Zeiger
  wird also am Ende der Funktion wieder vernichtet. Aber über diesen Zeiger kann auf das
  eigentliche Objekt zugegriffen (und damit auch verändert) werden. Es wird also nicht
  das Objekt selber übergeben, sondern ein Zeiger auf das Objekt.
  Bei den Parametern wird vor dem Parameternamen ein Stern gesetzt, da ja ein Zeiger
  übergeben wird; der Datentyp dagegen wird nicht geändert. Beim Aufruf selber
  wird vor dem Parameternamen der Adressoperator ('&') gesetzt.
  Dadurch wird ein Zeiger auf das eigentliche Objekt erzeugt und der Funktion übergeben.
  Das obige Beispiel wird nun so geändert, dass die Zahl per Zeiger an die Funktion
  übergeben wird.
  Beispiel:
   kap09_06.c
  kap09_06.c
  
  01 #include <stdio.h>
  02 
  03 int Addiere_3(int *);
  04 
  05 int main()
  06 {
  07    int Ergebnis, Zahl = 2;
  08 
  09    printf("Wert von Zahl = %i\n", Zahl);
  10    Ergebnis = Addiere_3(&Zahl);
  11    printf("Ergebnis 'Addiere_3(&Zahl)' = %i\n", Ergebnis);
  12    printf("Wert von Zahl = %i (veraendert!!!)\n", Zahl);
  13 
  14    return 0;
  15 }
  16 
  17 int Addiere_3(int *x)
  18 {
  19    *x += 3;
  20    return *x;
  21 }
  
  Rückgabewerte
  Der Rückgabewert wird innerhalb der Funktion mit dem Befehl
  return Wert; angegeben, wobei Wert
  der Rückgabewert ist. Gleichzeitig beendet dieser Befehl die Funktion, unabhängig
  davon, ob weitere Anweisungen folgen oder nicht. Als Beispiel wird noch einmal das obige
  Beispiel verwendet.
  Beispiel:
   kap09_07.c
  kap09_07.c
  
  01 #include <stdio.h>
  02 
  03 int Addiere_3(int);
  04 
  05 int main()
  06 {
  07    int Ergebnis, Zahl = 2;
  08 
  09    printf("Wert von Zahl = %i\n", Zahl);
  10    Ergebnis = Addiere_3(Zahl);
  11    printf("Ergebnis 'Addiere_3(Zahl)' = %i\n", Ergebnis);
  12    printf("Wert von Zahl = %i (unveraendert)\n", Zahl);
  13 
  14    return 0;
  15 }
  16 
  17 int Addiere_3(int x)
  18 {
  19    x += 3;
  20    return x;
  21    printf("STOP!"); /* Diese Anweisung wird nie ausgefuehrt! */
  22 }
  
  Bei der Rückgabe von Zeigern muss darauf geachtet werden, dass das dazugehörige
  Objekt auch in der aufrufenden Funktion existiert. Wenn z.B. ein Zeiger auf eine lokale
  Variable zurückgegeben wird, greift die aufrufende Funktion auf einen Speicherbereich
  zu, der eventuell bereits von einem anderen Programm oder einer anderen Funktion verwendet
  wird. Das Ergebnis ist dann u.U. falsch. Im schlimmsten Fall kann sogar ein Systemabsturz
  erzeugt werden! Das folgende Beispiel zeigt, wie es falsch ist, obwohl der Compiler
  u.U. keine Fehler und keine Warnungen anzeigt und das Programm sogar läuft.
  Beispiel:
   kap09_08.c
  kap09_08.c
  
  01 #include <stdio.h>
  02 
  03 int *Maximum(int, int);
  04 
  05 int main()
  06 {
  07    int x = 17, y = 4, *zp1, *zp2, z;
  08 
  09    zp1 = Maximum(x, y);
  10    zp2 = Maximum(y, x);
  11    z = *zp1;          /* Ergebnis 1. Fkt.aufruf zwischenspeichern */
  12    printf("x = %i\n", x);
  13    printf("y = %i\n", y);
  14    printf("z = %i\n", z);               /* Ergebnis 1. Fkt.aufruf */
  15    printf("Maximum(%i, %i) = %i\n", x, y, *zp1); /* 1. Fkt.aufruf */
  16    printf("Maximum(%i, %i) = %i\n", y, x, *zp2); /* 2. Fkt.aufruf */
  17 
  18    return 0;
  19 }
  20 
  21 int *Maximum(int a, int b)
  22 {
  23    /* a und b sind lokale Kopien!!! */
  24    return a > b ? &a : &b; /* Fehler!!! */
  25 }
  
  Zuerst wird der Zeiger zp1 auf die lokale Variable a gesetzt (da x > y
  ist). Anschließend wird die gleiche Funktion noch einmal aufgerufen. Dabei werden die
  lokalen Variablen a und b, die mit hoher
  Wahrscheinlichkeit die gleichen Speicheradressen belegen, auf andere Werte gesetzt. Damit zeigt
  auch zp1 auf einen anderen Wert. Damit ist das Ergebnis in
  z und in *zp1 falsch!
  
9.4. Rekursiver Funktionsaufruf
  Der Aufruf einer Funktion durch sich selbst wird Rekursion genannt.
  Eine Rekursion muss irgendwann auf eine Abbruchbedingung stoßen, damit die
  Rekursion nicht unendlich ist. Bei einer unendlichen Rekursion wird irgendwann ein
  sogenannter Stacküberlauf (stack overflow)
  erzeugt; das Programm wird damit abgebrochen.
  Als Beispiel für die Verwendung einer Rekursion wird die Quersumme einer ganzen Zahl
  berechnet: Eine Funktion ermittelt die letzte Ziffer einer Zahl, addiert diese zur
  Quersumme und ruft sich selbst mit den restlichen Ziffern wieder auf. Das Prinzip dieser
  Funktion lässt sich in zwei Sätze zusammenfassen:
  1. Die Quersumme der Zahl 0 ist gleich 0. Dies ist die Abbruchbedingung für die
  Rekursion!
  2. Die Quersumme einer Zahl ist gleich der letzten Ziffer plus der Quersumme der Zahl, die
  um diese Ziffer gekürzt wurde.
  Die Quersumme von 873956 ist also gleich 6 plus der Quersumme von 87395 und ist damit
  gleich 6 plus 5 plus der Quersumme von 8739 usw. Auf jede Quersumme wird der Satz 2
  angewandt, bis der Satz 1 gilt. Satz 1 gilt dann, wenn alle Ziffern von der Zahl abgetrennt
  wurden. Die letzte Ziffer der Zahl wird durch modulo 10 (Divisionsrest) abgetrennt.
  Die Zahl ohne der letzten Ziffer wird durch eine ganzzahlige Division durch 10
  erhalten.
  Beispiel:
   kap09_09.c
  kap09_09.c
  
  01 #include <stdio.h>
  02 
  03 int Quersumme(unsigned long);
  04 
  05 int main()
  06 {
  07    unsigned long Zahl;
  08 
  09    printf("Bitte eine positive ganze Zahl eingeben: ");
  10    scanf("%lu", &Zahl);
  11    printf("Quersumme von %u ist %i\n", Zahl, Quersumme(Zahl));
  12 
  13    return 0;
  14 }
  15 
  16 int Quersumme(unsigned long x)
  17 {
  18    int letzteZiffer = 0;
  19 
  20    if (!x) /* if (x == 0) Abbruchbedingung */
  21       return 0;   /* Abbruch der Rekursion */
  22    else
  23    {
  24       letzteZiffer = x % 10;  /* modulo 10 */
  25       return letzteZiffer + Quersumme(x / 10); /* Rekursion */
  26    }
  27 }
  
  Die Funktion Quersumme kann auch nicht-rekursiv geschrieben werden, in
  dem eine Schleife verwendet wird. Diese Variante wird iterativ
  genannt.
   kap09_10.c
  kap09_10.c
  
  01 #include <stdio.h>
  02 
  03 int Quersumme(unsigned long);
  04 
  05 int main()
  06 {
  07    unsigned long Zahl;
  08 
  09    printf("Bitte eine positive ganze Zahl eingeben: ");
  10    scanf("%lu", &Zahl);
  11    printf("Quersumme von %u ist %i\n", Zahl, Quersumme(Zahl));
  12 
  13    return 0;
  14 }
  15 
  16 int Quersumme(unsigned long x)
  17 {
  18    int QSumme = 0;
  19 
  20    while (x > 0)
  21    {
  22       QSumme += x % 10; /* letzte Ziffer addieren  */
  23       x /= 10;          /* letzte Ziffer abtrennen */
  24    }
  25    return QSumme;       /* Rueckgabe der Quersumme */
  26 }
  
9.5. Zeiger auf Funktionen
  Steht zum Zeitpunkt der Compilierung noch nicht fest, welche Funktion zur Laufzeit
  aufgerufen werden soll (z.B. wenn der Anwender zur Laufzeit erst die gewünschte
  Funktion auswählt) oder an welcher Adresse die Funktion steht (z.B. wenn Funktionen
  zur Laufzeit nachgeladen werden und daher deren Adressen beim Kompilieren noch nicht
  bekannt sind), wird mit Zeigern auf Funktionen gearbeitet.
  Das folgende Beispiel soll den Umgang mit Zeigern auf Funktionen verdeutlichen:
  Beispiel:
   kap09_11.c
  kap09_11.c
  
  01 #include <stdio.h>
  02 #include <math.h>
  03 
  04 void Funktionswerte(double (*)(double));
  05 
  06 int main()
  07 {
  08    double (*TrigFkt)(double); /* Zeiger auf Funktion, die ein
  09                                  double als Parameter erhaelt
  10                                  und ein double zurueckgibt.      */
  11 
  12    TrigFkt = sin;             /* Zeiger auf sin-Funktion zuweisen */
  13    Funktionswerte(TrigFkt);
  14 
  15    return 0;
  16 }
  17 
  18 void Funktionswerte(double (*Fkt)(double))
  19 {
  20    static const double PI = 4 * atan(1);
  21    double x;
  22 
  23    for (x = 0; x < PI; x += 0.01)
  24       printf("f(%5.2f) = %5.2f\n", x, Fkt(x));
  25 }
  
  Auch ein Array von Zeigern auf Funktionen ist möglich. Dazu wird gleich hinter dem
  Zeigernamen der Index angegeben.
  Beispiel:
   kap09_12.c
  kap09_12.c
  
  01 #include <stdio.h>
  02 #include <math.h>
  03 
  04 void Funktionswerte(double (*)(double));
  05 
  06 int main()
  07 {
  08    double (*TrigFkt[2])(double); /* Array von Zeigern auf Funktionen,
  09                                     die ein double als Parameter
  10                                     erhalten und ein double
  11                                     zurueckgeben.                  */
  12 
  13    TrigFkt[0] = sin;             /* Zeiger auf sin-Fkt. zuweisen   */
  14    TrigFkt[1] = cos;             /* Zeiger auf cos-Fkt. zuweisen   */
  15    Funktionswerte(TrigFkt[0]);
  16    Funktionswerte(TrigFkt[1]);
  17 
  18    return 0;
  19 }
  20 
  21 void Funktionswerte(double (*Fkt)(double))
  22 {
  23    static const double PI = 4 * atan(1);
  24    double x;
  25 
  26    for (x = 0; x < PI; x += 0.01)
  27       printf("f(%5.2f) = %5.2f\n", x, Fkt(x));
  28 }
  
  Zeiger auf Funktionen scheinen aber nur unzureichend standardisiert zu sein, da einige
  Compiler diese Beispiele nicht korrekt verarbeiten können.
  
9.6. Die Funktion main()
  Die Funktion main() - das Hauptprogramm - ist eine spezielle Funktion.
  Jedes C-/C++-Programm startet definitionsgemäß mit main(),
  so dass main() in jedem Programm genau einmal vorhanden sein
  muss. Der Rückgabewert ist standardmäßig int,
  aber viele Compiler lassen auch void zu (dann ist natürlich auch
  kein return 0; mehr erlaubt!). Die main()-Funktion
  kann nicht überladen werden. Die zwei folgenden Formen werden von allen Compilern
  unterstützt.
  einfache Form:
  
  int main()
  {  ...
     return 0; /* Exit-Code */
  }
  
  allgemeine (komplexere) Form:
  
  int main(int argc, char* argv[])
  {  ...
     return 0; /* Exit-Code */
  }
  
  In der zweiten Form werden zwei Parameter übergeben. Über diese beiden Parameter
  kann auf alle Kommandozeilenparameter zugegriffen werden, die beim
  Aufruf des Programms angegeben wurden. Das folgende Beispiel startet das Programm
  prog mit 4 Kommandozeilenparameter.
  Beispiel:
  ./prog 1 Test 547.32 3
  Der erste Parameter argc ist die Anzahl der Kommandozeilenparameter
  einschließlich des Programmaufrufs, im Beispiel gleich 5. Der zweite Parameter ist
  ein String-Array, also ein Array von Zeichenketten. In diesen Zeichenketten stehen die
  einzelnen Kommandozeilenparameter. Für das obige Beispiel sind folgende Werte in
  diesem Array gespeichert:
   argv[0] = "./prog"
 argv[1] = "1"
 argv[2] = "Test"
  argv[3] = "547.32"
 argv[4] = "3"
 argv[5] = NULL
  Wichtig: Auch die Zahlen-Kommandozeilenparameter werden hier als Texte
  gespeichert.
  Nun folgt ein Beispielprogramm, dass die Anzahl sowie alle Kommandozeilenparameter
  auflistet.
  Beispiel:
   kap09_13.c
  kap09_13.c
  
  01 #include <stdio.h>
  02 
  03 int main(int argc, char *argv[])
  04 {
  05    int i = 1;
  06 
  07    printf("Programmaufruf: %s\n", argv[0]);
  08    printf("Anzahl Kommandozeilenparameter: %i\n", argc - 1);
  09    printf("Kommandozeilenparameter:\n");
  10    while (argv[i])
  11    {
  12       printf("%2i: %s\n", i, argv[i]);
  13       i++;
  14    }
  15 
  16    return 0;
  17 }
  
  Voriges Kapitel: 8. Zeiger
  
Nächstes Kapitel: 10. Präprozessor-Befehle

