10 Funktionen

Programmcode wird in C immer in Form von Funktionen definiert; eine formale Unterscheidung zwischen Hauptprogramm, Unterprogrammen und Funktionen wie in anderen Sprachen gibt es nicht (Pascal: PROCEDURE/FUNCTION, FORTRAN: SUBROUTINE/FUNCTION). Dafür müssen Funktionen nicht unbedingt einen Wert zurückgeben. Geben sie einen Wert zurück, muß der Aufrufer den Wert nicht verwenden.

In FORTRAN etwa ist folgendes streng verboten:

        ...
        INTEGER FUNCTION summe( a, b )
        INTEGER a, b
        summe = a + b
        END
        ...
        INTEGER   summe;
*         erlaubt:
        a = summe( 1, 2 );
*         verboten:
        CALL summe( 3, 4 );
        ...

Das Gegenstück in C ist aber vollkommen legal20:

/* ... */
int summe( int a, b )
{
    return a + b;
}
/* ... */
    a = summe( 1, 2 );  /*  erlaubt            */
    summe( 3, 4 );      /* auch erlaubt        */
/* ... */

Ein Hauptprogramm im Sinne von anderen Sprachen gibt es nicht; die Programmausführung beginnt immer bei einer Funktion namens main().

Funktionen dürfen --im Gegensatz zu Pascal oder Modula2-- nicht innerhalb einer anderen Funktion definiert werden. Alle Funktionen in einem C-Quelltext stehen hintereinander und nicht ineinander geschachtelt.

Funktionsdefinitionen bestehen aus einem Kopf, in dem die Deklaration erfolgt (gegebenenfalls Attribute, der Typ des Rückgabewerts, der Name der Funktion, ein paar runde Klammer ((...)), die bei Bedarf die Parameter deklarieren) und einem Block, in dem lokale Variablen und der Funktionsablauf definiert werden. Ein Block ist in C immer in geschweifte Klammern ({...}) eingeschlossen (das entspricht dem Paar BEGIN...END in Pascal).

Beispiel:

/* fupotenz.c 30. 7.95 kw
 */

float potenz( float basis, int exponent )
{
  int i;
  float ergebnis;

  ergebnis = 1.0;
  for( i=0; i<exponent; i=i+1 )
  {
    ergebnis = ergebnis*basis;
  }
  return ergebnis;
}

Im Funktionskopf wird vereinbart, daß die Funktion namens potenz() einen Rückgabewert vom Typ float liefert (Gleitkommazahl in einfacher Genauigkeit) und dazu einen Parameter vom Typ float und einen vom Typ int erhält.

Im Funktionsrumpf (äußeres {}-Paar) werden erst die Variablen vereinbart (i, ergebnis) und dann der eigentliche Programmcode definiert.

Dieser wiederum besteht aus einer Zuweisung (ergebnis = 1.0;), einer for-Schleife und einer return-Anweisung.

In der for-Schleife wird zuerst i auf den Wert 0 gesetzt. Dann wird die Folge

durchgeführt, bis die Schleife wegen Nichterfüllung der Laufbedingung verlassen wird.

Mit der return-Anweisung wird die Abarbeitung der Funktion beendet und der Wert zurückgeliefert, der hinter dem return steht. Ist als Rückgabewert einer Funktion void vereinbart, dann darf hinter der return-Anweisung kein Rückgabewert stehen!

Lokale Variablen von Funktionen (wie hier i) stehen nur während des Abarbeitens der aktuellen Funktion zur Verfügung und können beim nächsten Aufruf derselben Funktion in der Regel ganz andere Werte haben. Das kann man aber auch anders haben; siehe Attributangaben.

Das eigentliche Hauptprogramm ist auch nur eine Funktion, die einen Wert vom Typ int liefert. In aller Regel sollte das der Wert 0 sein. Diese Funktion hat den Namen main() und spielt die Rolle des Hauptprogramms in anderen Sprachen.


10.0.0.1 Funktionsparameter

müssen in C nicht unbedingt deklariert werden, es ist aber immer empfehlenswert, um wenigstens einige Fehlermöglichkeiten auszuschließen.

Werden Parameter nicht deklariert, bevor eine Funktion aufgerufen wird, dann schließt der Compiler aus den übergebenen Parametern auf den jeweiligen Typ, den die Funktion erwartet. char-Parameter werden dann zu int gewandelt und float zu double. Felder werden als Zeiger auf das erste Element übergeben. Alle anderen Parameter werden (als Kopie, call by value, siehe unten) unverändert an eine Funktion weitergereicht21.

Die Deklaration einer Funktion könnte etwa so aussehen:

double ldurchi( long l, int i );
Das bedeutet, daß die Funktion ldurchi() zwei Parameter erhält, nämlich den ersten vom Typ long, und einen zweiten vom Typ int. Weiterhin besagt die Vereinbarung, daß die Funktion ein Resultat vom Typ double liefert.

Nach der obigen Deklaration könnte man die Funktion beispielsweise so aufrufen:

printf( "%f", ldurchi( 2, 3 ) );
Hätte man die Funktion nicht deklariert, dann hätte der Compiler keine reelle Chance, die richtigen Datentypen für den Aufruf zu erraten. In diesem Fall würde er annehmen, daß zwei Parameter vom Typ int übergeben werden sollen, weil ja solche Parameter verwendet werden, und als Rückgabewert den Standard int vermuten. Der Erfolg eines Aufrufs wäre dann sehr zweifelhaft.

C kennt eigentlich nicht die Unterscheidung zwischen call by value und call by reference. Letztere sind in Pascal die als VAR vereinbarten Funktionsparameter. In FORTRAN gibt es nur call by reference, das heißt daß Änderungen eines Parameters innerhalb eines Unterprogramms auch die entsprechende Variable beim aufrufenden Modul ändern.

In C gibt es formal nur Parameter, die mit dem call by value -Mechanismus übergeben werden.

Einzige Ausnahmen davon sind Felder und Funktionen (diese beiden werden generell als Adreßparameter (call by reference) übergeben).

Wird also eine Funktion mit Parametern aufgerufen, dann erhält sie in der Regel tatsächlich nur Kopien der Parameter. Ändert eine Funktion die Werte ihrer Parameter, dann wirkt sich dies nicht beim Aufrufer aus.

/* fu55.c 30. 7.95 kw
 *
 * Die Funktion f() erhoeht ihren Parameter und beendet
 * sich dann gleich wieder.
 */
void f( int wert )
{
  printf( "%d\n", wert ); /* Den Parameter ausgeben      */
  wert = wert + 1;        /* und erhoehen.               */
}

main()
{
  int     i;              /* Variable i vereinbaren      */

  i = 5;                  /* Den Wert 5 zuweisen         */
  f( i );                 /* Funktion f aufrufen         */
  printf( "%d\n", i );    /* Den Wert von i ausgeben     */
}
gibt zweimal die Zahl 5 aus, obwohl die Funktion f() ihren Parameter um eins erhöht:
5
5
f() hat tatsächlich keinen Zugriff auf die Variable i aus main(), sondern hat nur ein Kopie davon zur Verfügung.

C wäre aber nicht C, wenn man dieses Verhalten nicht umgehen könnte.

Um call by reference zu erzwingen, kann man an die Funktion statt der Variablen deren Adresse übergeben und innerhalb der Funktion statt des int-Parameters einen solchen vom Typ int * verwenden, über den man auf die Variable zugreift. Dann wirken sich Änderungen an der Variable auch beim Aufrufer aus, da über den Zeiger ja die Variable selbst manipuliert werden kann:

/* fu56.c 30. 7.95 kw
 */

void f( int *zeig )          /* Statt des Werts jetzt einen
                              * Zeiger darauf.
                              */
{
  printf( "%d\n", *zeig );   /* An den eigentlichen Wert    */
  (*zeig)++;                 /* kommen wir jetzt mit *zeig  */
                             /* heran (das, worauf zeig     */
                             /* zeigt).                     */
}

main()
{
  int     i;
  i = 5;
  f( &i );                   /* Nicht den Wert uebergeben,
                              * sondern seine Adresse (&i)
                              */
  printf( "%d\n", i );
}
erzeugt die Ausgabe:
5
6
Die Variable i des Hauptprogramms wurde also tatsächlich manipuliert.


10.0.0.2 Rückgabewert von Funktionen:

Soll eine Funktion einen Wert zurückgeben, dann ist sie natürlich erstmal mit dem entsprechenden Funktionskopf zu vereinbaren. Ist bei der Deklaration oder Definition vor dem Funktionsnamen kein Datentyp angegeben, dann wird vom Compiler der Typ int für den Rückgabewert angenommen.

Will man dies verhindern, weil die Funktion gar keinen Wert zurückgeben soll, dann wird sie mit dem Typ void vereinbart. Hinter den return-Anweisungen der Funktion darf dann kein Rückgabewert angegeben sein.

Soll die Funktion einen Wert liefern, dann muß man hinter den return-Anweisungen einen Ausdruck angeben, der das gewünschte Ergebnis liefert.

Beispiel:

double pi()
{
  return 3.14159265;
}



Unterabschnitte
AnyWare@Wachtler.de