← Zurück zur Übersicht

Vorlesung 9

Strukturen - Teil 2: Arrays & Funktionen

Fortgeschrittene Algorithmen und Programmierung in C

Wie arbeitet man mit mehreren Strukturen und übergibt sie an Funktionen?

Übersicht: Teil 2

  • Wiederholung: Teil 1
  • Arrays von Strukturen
  • Visualisierung: Speicher-Layout
  • Funktionen mit Strukturen (Call by Value)
  • Zeiger auf Strukturen
  • Der Pfeil-Operator (->)
  • Call by Reference mit Strukturen
  • Strukturen zurückgeben
  • Praxisbeispiele mit Visualisierungen

📝 Wiederholung: Teil 1

Was wir können:

  • Strukturen definieren mit struct
  • Variablen erstellen vom Struktur-Typ
  • Punkt-Operator für Zugriff
  • strcpy() für String-Komponenten
struct Person { char name[50]; int alter; }; struct Person p1; strcpy(p1.name, "Anna"); p1.alter = 23;

Heute: Mehrere Strukturen verwalten und an Funktionen übergeben!

📚 Arrays von Strukturen: Warum?

⚠️ Problem: Viele einzelne Variablen

struct Person student1, student2, student3, student4, student5; // Bei 30 Studierenden? 100? Unmöglich!

✅ Lösung: Array von Strukturen

struct Person klasse[30]; // 30 Personen in EINEM Array!

Jedes Array-Element ist eine komplette Struktur!

Deklaration: Array von Strukturen

struct Person { char name[50]; int alter; }; int main() { // Array mit 3 Personen struct Person klasse[3]; return 0; }

Visualisierung im Speicher:

klasse[0]
name: [50 Bytes]
alter: [4 Bytes]
klasse[1]
name: [50 Bytes]
alter: [4 Bytes]
klasse[2]
name: [50 Bytes]
alter: [4 Bytes]

Initialisierung mit Werten

struct Person klasse[3] = { {"Anna", 20}, // Index 0 {"Ben", 22}, // Index 1 {"Clara", 21} // Index 2 };

Syntax: Jede Person in eigenen { }, alle zusammen in äußeren { }

Mit Daten gefüllt:

klasse[0]
name: "Anna"
alter: 20
klasse[1]
name: "Ben"
alter: 22
klasse[2]
name: "Clara"
alter: 21

Zugriff: Array + Punkt-Operator

Syntax: arrayName[index].komponente

// Schreiben: strcpy(klasse[0].name, "Anna"); klasse[0].alter = 20; // Lesen: printf("%s", klasse[0].name); printf("%d", klasse[0].alter);

Schritt für Schritt:

1 klasse[0] → Erste Person
2 .name → Name dieser Person
3 .alter → Alter dieser Person

Mit Schleife durchlaufen

struct Person klasse[3] = { {"Anna", 20}, {"Ben", 22}, {"Clara", 21} }; for (int i = 0; i < 3; i++) { printf("Person %d: %s, %d\n", i + 1, klasse[i].name, klasse[i].alter); }
Person 1: Anna, 20 Person 2: Ben, 22 Person 3: Clara, 21

Schleifendurchlauf:

i=0
Anna
i=1
Ben
i=2
Clara

Eingabe mit scanf()

struct Person klasse[3]; for (int i = 0; i < 3; i++) { printf("Person %d:\n", i + 1); printf("Name: "); scanf("%s", klasse[i].name); // OHNE & (String!) printf("Alter: "); scanf("%d", &klasse[i].alter); // MIT & (int!) }

⚠️ OHNE &

scanf("%s", klasse[i].name)

Strings sind bereits Adressen!

✅ MIT &

scanf("%d", &klasse[i].alter)

int/double brauchen &

⚙️ Funktionen mit Strukturen

Warum Strukturen an Funktionen übergeben?

  • Code organisieren und wiederverwendbar machen
  • Komplexe Operationen auslagern
  • Programme übersichtlicher gestalten

Call by Value

Kopie wird übergeben

Original bleibt unverändert

Call by Reference

Adresse wird übergeben

Original kann geändert werden

Call by Value: Beispiel

void zeigePerson(struct Person p) { printf("Name: %s\n", p.name); printf("Alter: %d\n", p.alter); } int main() { struct Person p1 = {"Anna", 23}; zeigePerson(p1); return 0; }

Was passiert:

p1 (Original)
name: "Anna"
alter: 23
↓ KOPIE ↓
p (Kopie in Funktion)
name: "Anna"
alter: 23

Call by Value: Das Problem

// Diese Funktion ändert NICHT! void aendereAlter(struct Person p) { p.alter = 30; // Nur Kopie! } int main() { struct Person p1 = {"Anna", 23}; aendereAlter(p1); printf("%d\n", p1.alter); return 0; }
23 ← Nicht 30!

Visualisierung:

p1 (Original)
alter: 23 ✓
↓ Kopie erstellt ↓
p (Kopie)
alter: 23 → 30
↓ Kopie wird gelöscht ↓

Änderung verloren!

Problem: Call by Value ändert nur die Kopie, nicht das Original!

🎯 Zeiger auf Strukturen

struct Person p1 = {"Anna", 23}; // Zeiger auf Struktur erstellen // ptr = "pointer" (Zeiger) struct Person *ptr = &p1; // Zugriff über Zeiger - 2 Möglichkeiten: (*ptr).alter = 25; // Methode 1: umständlich ptr->alter = 25; // Methode 2: elegant ✓

Warum "ptr"?

Kurz für "pointer" (Zeiger). Konvention: Zeiger-Variablen heißen oft ptr, p, oder pPerson.

Speicherbild:

ptr
Adresse: 0x1000
p1 (0x1000)
name: "Anna"
alter: 25

Warum ist -> besser?

  • Kürzer und lesbarer
  • Keine Klammern nötig
  • Industriestandard

Der Pfeil-Operator (->)

Zwei Schreibweisen - gleiches Ergebnis:

❌ Umständlich

(*ptr).alter = 25; (*ptr).name (*ptr).groesse

Klammern nötig wegen Priorität!

✅ Elegant

ptr->alter = 25; ptr->name ptr->groesse

Pfeil-Operator = Standard!

ptr->alter ist identisch mit (*ptr).alter

Vergleich: Punkt vs. Pfeil

Situation Operator Beispiel
Normale Struktur-Variable Punkt (.) p1.alter = 25;
Zeiger auf Struktur Pfeil (->) ptr->alter = 25;

Direkte Variable:

struct Person p1; p1.alter = 25; // Punkt!

Über Zeiger:

struct Person *ptr = &p1; ptr->alter = 25; // Pfeil!

Call by Reference: Lösung!

// Mit Zeiger-Parameter void aendereAlter(struct Person *p, int neuesAlter) { p->alter = neuesAlter; } int main() { struct Person p1 = {"Anna", 23}; printf("Vorher: %d\n", p1.alter); aendereAlter(&p1, 30); printf("Nachher: %d\n", p1.alter); return 0; }
Vorher: 23 Nachher: 30 ← Geändert!

Visualisierung:

1 &p1 übergibt Adresse
p
= &p1
p1 (Original!)
alter: 23 → 30
2 p->alter ändert Original!

Vollständiges Beispiel: Geburtstag

#include <stdio.h> #include <string.h> struct Person { char name[50]; int alter; }; // Erhöht Alter um 1 void geburtstag(struct Person *p) { p->alter++; printf("Happy Birthday, %s!\n", p->name); } int main() { struct Person p = {"Lisa", 25}; printf("Alter: %d\n", p.alter); geburtstag(&p); // & nicht vergessen! printf("Alter: %d\n", p.alter); return 0; }
Alter: 25 Happy Birthday, Lisa! Alter: 26

Ablauf:

Vorher
name: "Lisa"
alter: 25
↓ geburtstag(&p) ↓
Nachher
name: "Lisa"
alter: 26

Arrays von Strukturen an Funktionen

void zeigeAlle(struct Person arr[], int anzahl) { for (int i = 0; i < anzahl; i++) { printf("%s: %d\n", arr[i].name, arr[i].alter); } } int main() { struct Person klasse[3] = { {"Anna", 20}, {"Ben", 22}, {"Clara", 21} }; zeigeAlle(klasse, 3); return 0; }
Anna: 20 Ben: 22 Clara: 21

Wichtig:

  • Array-Name ohne [] übergeben
  • Größe extra als Parameter
  • Arrays sind immer Call by Reference!

Strukturen zurückgeben

struct Person erstellePerson( char *name, int alter) { struct Person p; strcpy(p.name, name); p.alter = alter; return p; // Struktur zurückgeben } int main() { struct Person p1; p1 = erstellePerson("Tom", 28); printf("%s: %d Jahre\n", p1.name, p1.alter); return 0; }
Tom: 28 Jahre

Ablauf:

1 Funktion erstellt lokale Struktur p
2 Füllt p mit Daten
3 Gibt Kopie von p zurück
4 p1 erhält die Kopie

Praktisch für "Konstruktor"-Funktionen!

⚡ Performance: Value vs. Reference

Aspekt Call by Value Call by Reference
Übergeben wird Komplette Kopie Nur Adresse (8 Bytes)
Bei 100 Bytes Struktur 100 Bytes kopiert 8 Bytes kopiert
Original änderbar? Nein Ja
Geschwindigkeit Langsamer bei großen Structs Immer schnell

Empfehlung

Bei Strukturen mit mehr als 3-4 Komponenten: Call by Reference verwenden!

📊 Praxis: Studierendendatenbank

struct Student { char name[50]; int matrikelnr; double note; }; double durchschnitt(struct Student s[], int n) { double summe = 0; for (int i = 0; i < n; i++) { summe += s[i].note; } return summe / n; } int main() { struct Student kurs[3] = { {"Lisa", 12345, 1.7}, {"Max", 12346, 2.3}, {"Tim", 12347, 1.3} }; printf("Ø: %.2f\n", durchschnitt(kurs, 3)); }
Ø: 1.77

Berechnung:

Lisa
Note: 1.7
Max
Note: 2.3
Tim
Note: 1.3
(1.7 + 2.3 + 1.3) / 3 = 1.77

🛒 Praxis: Warenkorb

struct Produkt { char name[50]; double preis; int menge; }; double gesamtpreis(struct Produkt w[], int n) { double summe = 0; for (int i = 0; i < n; i++) { summe += w[i].preis * w[i].menge; } return summe; } int main() { struct Produkt korb[3] = { {"Apfel", 0.50, 5}, {"Brot", 2.50, 2}, {"Milch", 1.20, 3} }; printf("Gesamt: %.2f€\n", gesamtpreis(korb, 3)); }
Gesamt: 11.10€

Berechnung:

Apfel 0.50€ × 5 = 2.50€
Brot 2.50€ × 2 = 5.00€
Milch 1.20€ × 3 = 3.60€
Summe: = 11.10€

🔍 Praxis: Suche im Array

#include <stdio.h> #include <string.h> struct Person { char name[50]; int alter; }; // Suche Person nach Name struct Person* findePerson( struct Person arr[], int n, char *suchname) { for (int i = 0; i < n; i++) { if (strcmp(arr[i].name, suchname) == 0) { return &arr[i]; // Gefunden! } } return NULL; // Nicht gefunden } int main() { struct Person arr[3] = { {"Anna", 20}, {"Ben", 25}, {"Clara", 22} }; struct Person *p = findePerson(arr, 3, "Ben"); if (p != NULL) { printf("Alter: %d\n", p->alter); } return 0; }

Suchvorgang nach "Ben":

arr[0]
"Anna" ≠ "Ben"
arr[1] ✓
"Ben" == "Ben"
arr[2]
"Clara"

Intern: Rückgabe &arr[1]

Ausgabe: Alter: 25

❌ Häufige Fehler - Teil 1

Fehler 1: Falscher Operator

// FALSCH: struct Person *ptr = &p1; ptr.alter = 25; // ❌ Punkt! // RICHTIG: ptr->alter = 25; // ✅ Pfeil!

Fehler 2: & vergessen

// FALSCH: aendere(p1); // ❌ Wert! // RICHTIG: aendere(&p1); // ✅ Adresse!

Merke: Zeiger → Pfeil (->), Variable → Punkt (.)

❌ Häufige Fehler - Teil 2

Fehler 3: scanf bei Arrays

// FALSCH: scanf("%s", &arr[i].name); // ❌ scanf("%d", arr[i].alter); // ❌ // RICHTIG: scanf("%s", arr[i].name); // ✅ scanf("%d", &arr[i].alter); // ✅

Fehler 4: Struct in Funktion

// FALSCH: void f(Person p) {...} // ❌ // RICHTIG: void f(struct Person p) {...} // ✅ struct nicht vergessen!

✅ Best Practices

1. Strukturen vor main()

Damit alle Funktionen sie kennen

2. Call by Reference

Bei großen Strukturen immer

3. Pfeil-Operator

ptr->x statt (*ptr).x

4. Sprechende Namen

struct Student statt struct S

5. strcpy() für Strings

Niemals = für char[]

6. Array-Größe übergeben

Immer als extra Parameter

📝 Übungsaufgabe 1

Array von Büchern

Erstellen Sie eine Struktur Buch mit: titel (String), autor (String), seiten (int).

  • Erstellen Sie ein Array mit 3 Büchern
  • Geben Sie alle Bücher mit einer Schleife aus

Erwartete Ausgabe:

1. "Der Herr der Ringe" von Tolkien (1200 Seiten) 2. "Harry Potter" von Rowling (800 Seiten) 3. "1984" von Orwell (350 Seiten)

✅ Lösung Übungsaufgabe 1

#include <stdio.h> struct Buch { char titel[100]; char autor[50]; int seiten; }; int main() { struct Buch bibliothek[3] = { {"Der Herr der Ringe", "Tolkien", 1200}, {"Harry Potter", "Rowling", 800}, {"1984", "Orwell", 350} }; for (int i = 0; i < 3; i++) { printf("%d. \"%s\" von %s (%d Seiten)\n", i + 1, bibliothek[i].titel, bibliothek[i].autor, bibliothek[i].seiten); } return 0; }
1. "Der Herr der Ringe" von Tolkien (1200 Seiten) 2. "Harry Potter" von Rowling (800 Seiten) 3. "1984" von Orwell (350 Seiten)

📝 Übungsaufgabe 2

Funktion mit Call by Reference

Struktur Produkt mit: name (String), preis (double).

  • Schreiben Sie eine Funktion erhoehe_preis
  • Die Funktion erhöht den Preis um einen Prozentsatz
  • Nutzen Sie Call by Reference!

Funktionskopf:

void erhoehe_preis(struct Produkt *p, double prozent)

Beispielaufruf:

erhoehe_preis(&laptop, 10.0);

// Erhöht um 10%

✅ Lösung Übungsaufgabe 2

#include <stdio.h> struct Produkt { char name[50]; double preis; }; void erhoehe_preis(struct Produkt *p, double prozent) { p->preis = p->preis * (1 + prozent / 100); } int main() { struct Produkt laptop = {"MacBook", 1000.00}; printf("Vorher: %.2f Euro\n", laptop.preis); erhoehe_preis(&laptop, 10.0); // +10% printf("Nachher: %.2f Euro\n", laptop.preis); return 0; }
Vorher: 1000.00 Euro Nachher: 1100.00 Euro

Wichtig: Durch &laptop wird die Adresse übergeben. Die Funktion ändert das Original!

📝 Übungsaufgabe 3

Suche und Rückgabe

Struktur Student mit: name (String), matrikelnr (int), note (double).

  • Schreiben Sie eine Funktion, die einen Studierenden nach Matrikelnummer sucht
  • Rückgabe: Zeiger auf gefundenen Studierenden oder NULL

Funktionsprototyp:

struct Student* finde_student( struct Student arr[], int n, int gesuchte_matrikelnr );

✅ Lösung Übungsaufgabe 3

#include <stdio.h> struct Student { char name[50]; int matrikelnr; double note; }; struct Student* finde_student(struct Student arr[], int n, int gesuchte_matrikelnr) { for (int i = 0; i < n; i++) { if (arr[i].matrikelnr == gesuchte_matrikelnr) { return &arr[i]; // Gefunden! } } return NULL; // Nicht gefunden } int main() { struct Student studenten[3] = { {"Anna", 12345, 1.7}, {"Ben", 67890, 2.3}, {"Clara", 11111, 1.0} }; struct Student *gefunden = finde_student(studenten, 3, 67890); if (gefunden != NULL) { printf("Gefunden: %s (Note: %.1f)\n", gefunden->name, gefunden->note); } else { printf("Student nicht gefunden!\n"); } return 0; }
Gefunden: Ben (Note: 2.3)

📚 Zusammenfassung

Arrays von Strukturen

  • struct Person arr[n];
  • Zugriff: arr[i].name
  • Mit Schleife durchlaufen

Zeiger auf Strukturen

  • struct Person *ptr = &p;
  • Pfeil-Operator: ptr->alter

Call by Value

  • Kopie wird übergeben
  • Original bleibt unverändert

Call by Reference

  • Adresse wird übergeben
  • Original kann geändert werden
  • Schneller bei großen Strukturen

Ende Vorlesung 9

Strukturen meistern Sie nun!

Üben Sie mit den drei Übungsaufgaben.
Kombinieren Sie Arrays, Funktionen und Zeiger!

Nächste Vorlesung: Rekursion
Fortgeschrittene Algorithmen und Programmierung in C

1 / 34