Zeiger auf Strukturen
In der vorigen Übung hat die Funktion eins_verkaufen nur ihre Kopie des Produkts geändert. Die Lösung kennst du aus der Lektion über Zeiger: Gib der Funktion die Adresse der Variablen, und sie kann damit das Original ändern. Bei Strukturen funktioniert das genauso:
typedef struct {
char name[20];
double preis;
int anzahl;
} Produkt;
void anzahl_erhoehen(Produkt *artikel, int extra) {
(*artikel).anzahl += extra;
}Der Parameter artikel ist ein Zeiger auf ein Produkt. *artikel ist das Produkt, auf das er zeigt, und (*artikel).anzahl ist die Anzahl dieses Produkts. Die Klammern sind nötig: Der Punkt wird vor dem Stern ausgewertet, also würde *artikel.anzahl „der Member anzahl von artikel und dann *“ bedeuten, aber artikel ist ein Zeiger, keine Struktur. Deshalb hat C eine kürzere und klarere Schreibweise, den Pfeil ->, geschrieben als Minuszeichen und Größer-als-Zeichen:
#include <stdio.h>
typedef struct {
char name[20];
double preis;
int anzahl;
} Produkt;
void anzahl_erhoehen(Produkt *artikel, int extra) {
artikel->anzahl += extra;
}
int main(void) {
Produkt milch = {"Milch", 1.2, 3};
anzahl_erhoehen(&milch, 2);
printf("%s: %d\n", milch.name, milch.anzahl);
return 0;
}Das gibt aus:
Milch: 5artikel->anzahl bedeutet genau dasselbe wie (*artikel).anzahl: „der Member anzahl der Struktur, auf die artikel zeigt“. Programmierer schreiben den Pfeil, und wir auch. Die Funktion rufst du mit der Adresse des Produkts auf, &milch, wie bei jedem Zeigerparameter.
Wann schreibst du also einen Punkt und wann einen Pfeil? Sieh dir an, was links steht. Eine Struktur (milch, warenkorb[i] oder ein Parameter Produkt artikel) bekommt einen Punkt. Ein Zeiger auf eine Struktur (ein Parameter Produkt *artikel) bekommt einen Pfeil. Wenn du sie verwechselst, sagt dir der Compiler es und schlägt sogar das Richtige vor:
main.c:10:12: error: member reference type 'Produkt *' is a pointer; did you mean to use '->'?
10 | artikel.anzahl += extra;
| ~~~~~~~^
| ->Umgekehrt endet milch->anzahl mit member reference type 'Produkt' is not a pointer; did you mean to use '.'?. Und wenn du im Aufruf das & vergisst, anzahl_erhoehen(milch, 2), lautet der Fehler passing 'Produkt' to parameter of incompatible type 'Produkt *'; take the address with &: Die Funktion will eine Adresse, also übergib ihr eine mit &.
Ein Zeiger ist auch nützlich, wenn die Funktion die Struktur gar nicht ändert. Ein Strukturparameter bekommt bei jedem Aufruf eine Kopie der ganzen Struktur, und manche Strukturen sind groß. Ein Zeiger ist nur eine Adresse. Wenn die Funktion die Struktur nur liest, füge const hinzu, wie bei Arrays: In double zeilensumme(const Produkt *artikel) kann die Funktion artikel->preis lesen, aber nicht aus Versehen ändern.
Und ein Array von Strukturen? Eine Funktion bekommt das Array des Aufrufers selbst, keine Kopie, wie bei jedem Array. In einer Funktion mit dem Parameter Produkt warenkorb[] ist warenkorb[i] eine Struktur, also schreibst du einen Punkt, warenkorb[i].anzahl = 0;, und die Änderung bleibt im Array des Aufrufers. Um ein Element des Arrays an anzahl_erhoehen zu übergeben, gib ihr die Adresse des Elements: anzahl_erhoehen(&warenkorb[1], 2);.
So schreibst du es:
Anweisungen
- 1
Die Funktion
sieg_eintragensoll einem Team für einen Sieg 3 Punkte addieren, aber das Programm gibtLöwen: 5 Punkteaus. Ändere die Funktion so, dass sie einen Zeiger auf das Team bekommt, verwende darin->und rufe sie mit der Adresse vonloewenauf. Das Programm gibt dannLöwen: 8 Punkteaus. - 2
Schreibe unter
sieg_eintrageneine Funktionvoid spiel_eintragen(Team *team, int geschossen, int kassiert)für ein beliebiges Spiel. Wenn das Team mehr Tore geschossen als kassiert hat, hat es gewonnen und bekommt 3 Punkte; bei einem Unentschieden (gleich viele Tore) bekommt es 1 Punkt; bei einer Niederlage nichts. Rufe sie am Ende vonmain(vorreturn 0;) zweimal auf,spiel_eintragen(&loewen, 2, 2)undspiel_eintragen(&loewen, 3, 1), und gib das Team dann mit demselbenprintfnoch einmal aus. Die letzte Zeile istLöwen: 12 Punkte.