Zum Inhalt springen
C Anfรคnger 25 min

Funktionen & Scope

Funktionen in C: Deklaration vs. Definition, Prototypen in Headern, Pass-by-Value und wie du sauberen C-Code modular baust.

Aktualisiert:
Inhaltsverzeichnis

Funktionen & Scope in C

C-Funktionen sind schlank und direkt - aber die Trennung von Deklaration (Prototyp) und Definition ist etwas, das du in modernen Sprachen nicht mehr kennst.

Grundform

int addiere(int a, int b) {
    return a + b;
}

int main(void) {
    int summe = addiere(3, 4);
    printf("%d\n", summe);
    return 0;
}
  • Rueckgabetyp vor dem Namen
  • Parameter brauchen Typ und Namen
  • Semikolons am Ende jedes Statements

void - keine Rueckgabe

void gruessen(const char *name) {
    printf("Hallo, %s!\n", name);
}

Funktions-Prototypen

Wenn die Definition erst nach dem Aufruf steht, brauchst du einen Prototyp:

#include <stdio.h>

// Prototyp
int addiere(int a, int b);

int main(void) {
    printf("%d\n", addiere(3, 4));   // nutzt Prototyp
    return 0;
}

// Definition
int addiere(int a, int b) {
    return a + b;
}

Das ist wichtig, weil C der Reihe nach liest. Ohne Prototyp haette der Compiler vor der Definition keine Info ueber die Signatur.

In der Praxis stehen Prototypen in Header-Dateien (.h), damit mehrere Quelldateien sie teilen koennen.

Header und Source trennen

math_utils.h:

#ifndef MATH_UTILS_H
#define MATH_UTILS_H

int addiere(int a, int b);
int subtrahiere(int a, int b);

#endif

Die #ifndef / #define / #endif-Struktur ist ein Include-Guard - er verhindert, dass der Header doppelt eingelesen wird.

math_utils.c:

#include "math_utils.h"

int addiere(int a, int b) {
    return a + b;
}

int subtrahiere(int a, int b) {
    return a - b;
}

main.c:

#include <stdio.h>
#include "math_utils.h"

int main(void) {
    printf("%d\n", addiere(3, 4));
    printf("%d\n", subtrahiere(10, 4));
    return 0;
}

Kompilieren:

gcc main.c math_utils.c -o app

Pass-by-Value

C uebergibt Argumente per Kopie (Pass-by-Value):

void verdoppeln(int x) {
    x *= 2;     // aendert nur die Kopie
}

int main(void) {
    int y = 5;
    verdoppeln(y);
    printf("%d\n", y);   // immer noch 5!
    return 0;
}

Pass-by-Reference via Pointer

Wenn die Funktion den Wert aendern soll, uebergibst du einen Pointer:

void verdoppeln(int *x) {
    *x *= 2;    // Dereferenzieren und aendern
}

int main(void) {
    int y = 5;
    verdoppeln(&y);       // Adresse uebergeben
    printf("%d\n", y);    // jetzt 10
    return 0;
}

Das &y uebergibt die Adresse, *x liest/schreibt den Wert an dieser Adresse.

Arrays als Parameter

Arrays werden immer als Pointer uebergeben. Du brauchst die Groesse separat:

int summe(const int *zahlen, size_t n) {
    int total = 0;
    for (size_t i = 0; i < n; i++) {
        total += zahlen[i];
    }
    return total;
}

int main(void) {
    int werte[] = {1, 2, 3, 4, 5};
    size_t n = sizeof(werte) / sizeof(werte[0]);
    printf("%d\n", summe(werte, n));   // 15
    return 0;
}

Die alternative Schreibweise int zahlen[] ist gleichwertig zu int *zahlen - C behandelt Array-Parameter wie Pointer:

int summe(const int zahlen[], size_t n) { /* gleich */ }

Strings als Parameter

#include <string.h>

size_t laenge(const char *text) {
    return strlen(text);
}

int main(void) {
    printf("%zu\n", laenge("Hallo"));   // 5
    return 0;
}

const char * signalisiert: โ€œIch รคndere den String nicht.โ€

Mehrere Rueckgabewerte

C hat keine Multi-Return. Zwei gelaeufige Muster:

Out-Parameter via Pointer

void min_max(const int *zahlen, size_t n, int *min_out, int *max_out) {
    *min_out = zahlen[0];
    *max_out = zahlen[0];
    for (size_t i = 1; i < n; i++) {
        if (zahlen[i] < *min_out) *min_out = zahlen[i];
        if (zahlen[i] > *max_out) *max_out = zahlen[i];
    }
}

int main(void) {
    int zahlen[] = {3, 1, 4, 1, 5, 9, 2, 6};
    int mn, mx;
    min_max(zahlen, 8, &mn, &mx);
    printf("min=%d max=%d\n", mn, mx);
    return 0;
}

Struct als Rueckgabe

struct MinMax {
    int min;
    int max;
};

struct MinMax min_max(const int *zahlen, size_t n) {
    struct MinMax result = {zahlen[0], zahlen[0]};
    for (size_t i = 1; i < n; i++) {
        if (zahlen[i] < result.min) result.min = zahlen[i];
        if (zahlen[i] > result.max) result.max = zahlen[i];
    }
    return result;
}

Strukturen lernen wir gleich im Detail kennen.

static-Funktionen

Eine Funktion, die nur in der aktuellen Datei sichtbar sein soll:

static int hilfsfunktion(int x) {
    return x * 2;
}

Gleich wie static-Variablen: beschraenkt den Scope auf die Uebersetzungseinheit (.c-Datei). Hilft, Namenskonflikte zu vermeiden.

Rekursion

int fakultaet(int n) {
    if (n <= 1) return 1;
    return n * fakultaet(n - 1);
}

int main(void) {
    printf("%d\n", fakultaet(5));    // 120
    return 0;
}

Wie in anderen Sprachen: Achte auf die Abbruchbedingung, sonst Stack Overflow.

Praktisches Beispiel

string_utils.h:

#ifndef STRING_UTILS_H
#define STRING_UTILS_H

#include <stddef.h>

int zaehle_vokale(const char *text);
void zu_gross(char *text);

#endif

string_utils.c:

#include "string_utils.h"
#include <ctype.h>

int zaehle_vokale(const char *text) {
    int anzahl = 0;
    for (size_t i = 0; text[i] != '\0'; i++) {
        char c = (char) tolower((unsigned char) text[i]);
        if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
            anzahl++;
        }
    }
    return anzahl;
}

void zu_gross(char *text) {
    for (size_t i = 0; text[i] != '\0'; i++) {
        text[i] = (char) toupper((unsigned char) text[i]);
    }
}

main.c:

#include <stdio.h>
#include "string_utils.h"

int main(void) {
    char name[] = "Programmieren";
    printf("Vokale: %d\n", zaehle_vokale(name));

    zu_gross(name);
    printf("Gross: %s\n", name);
    return 0;
}

Zusammenfassung

  • typ name(typ param) { return wert; } ist die Grundstruktur
  • Prototypen in Headern, Definitionen in .c-Dateien
  • Include-Guards schuetzen vor Doppelinklude
  • Pass-by-Value ist Standard - Pointer fuer โ€œaendernโ€
  • Arrays werden immer als Pointer uebergeben - Groesse separat mitfuehren
  • Mehrere Rueckgabewerte ueber Out-Parameter oder Structs
  • static-Funktionen bleiben in der Datei

Im naechsten Kapitel kommen wir zum Herzstueck: Pointer und Arrays.

Zurรผck zum C Kurs