Funktionen
Funktionen sind wiederverwertbare Code-Schnipsel, die nahezu überall aufgerufen werden dürfen. Eine Funktion besteht dabei aus einem Namen, einem Rückgabewert und einer Liste an Parametern. Die beim Aufruf der Funktion übergebenen Parameter werden immer kopiert. Die Anzahl und Größe der Parameter sollte daher immer so klein wie möglich gehalten werden. Große Konstrukte wie Strukturen übergibt am daher typischerweise als Pointer.
Die main()-Funktion ist der Einsprungspunkt ins Programm und wird vom Betriebssystem gestartet. Wird die Funktion verlassen ist das Programm beendet.
Wir schauen uns nun ein paar Beispiele an, um die Herangehensweise bei Erstellen von Funktionen zu verstehen.
Beispiel 1
#include <stdio.h>
typedef struct fraction__T {
int numerator;
int denominator;
} fraction_t;
int add(int a, int b)
{
return a + b;
}
float fractionToFloat(const fraction_t *frac)
{
return ((float)frac->numerator)/((float)frac->denominator);
}
int main()
{
int a = 31;
int b = 11;
fraction_t frac = {
.numerator = 3,
.denominator = 5
};
int c = add(a, b);
float f = fractionToFloat(&frac);
printf("a + b = c | %d + %d = %d\n", a, b, c);
printf("%d / %d = %f\n", frac.numerator, frac.denominator, f);
return 0;
}
In diesem ersten Beispiel werden zwei Funktionen angelegt: "add" und "fractionToFloat". "add" ist dabei eine klassische Funktion. Sie nimmt zwei Parameter an und gibt einen Integer als Ergebnis zurück. "fractionToFloat" hingegen arbeit mit einer Struktur, die zur Reduktion der zu kopierenden Speichermenge als Pointer übergeben wird. Das "const" vor dem Parametertypen weist den Compiler außerdem darauf hin, dass der Wert von "frac" in der Funktion nicht verändert werden darf.
Beispiel 2
#include <stdio.h>
test()
{
return 5;
}
void greet()
{
printf("Hello, World!\n");
}
int main()
{
int a = test();
greet();
//int b = greet();
return 0;
}
Funktionen können auf die Rückgabe eines Ergebnis verzichten. In diesem Fall wird "void" als Rückgabetyp verwendet. Ohne angegebenen Rückgabetypen nimmt der Compiler "int" an, wie in Funktion "test()" zu sehen ist.
Beispiel 3
#include <stdio.h>
#include <errno.h>
#include <math.h>
#include <string.h>
int calcuate_square_root(double a, double *res)
{
if (a <= 0.0) {
return EINVAL;
}
*res = sqrt(a);
return 0;
}
int main()
{
double value = -9.0;
double result;
int error = calcuate_square_root(value, &result);
if (error == 0) {
// no error, everything works fine
printf("square root of %f is %f.\n", value, result);
}
else {
// an error occured
printf("ERROR: %s.\n", strerror(error));
}
return 0;
}
Ein typisches Entwicklungsmuster für Funktionen die fehlschlagen können ist es, einen Integer als Rückgabewert zu verwenden. Dieser Integer trägt im Fehlerfall einen Fehlercode. Läuft die Funktion fehlerfrei durch, wird 0 zurückgegeben. Benötigt eine solche Funktion einen Rückgabewert, so muss dieser über einen Pointer-Parameter erzeugt werden.