Übergabe von Struct-Zeigern
Teil des Abschnitts Logic & Flow der C-Journey von Coddy — Lektion 47 von 63.
Während die Übergabe von Structs als Wert gut für das Lesen von Daten funktioniert, wird sie bei großen Structs ineffizient, da die gesamte Struktur kopiert wird. Was noch wichtiger ist: Sie können das ursprüngliche Struct nicht innerhalb der Funktion ändern. Hier wird die Übergabe von Zeigern auf Structs unerlässlich.
Wenn Sie einen Pointer auf ein Struct anstelle des Structs selbst übergeben, erhält die Funktion die Speicheradresse des ursprünglichen Structs. Dieser Ansatz bietet zwei entscheidende Vorteile: Er ist effizienter (kein Kopieren) und ermöglicht es der Funktion, die Originaldaten zu ändern.
Die Syntax für eine Funktion, die einen Zeiger auf ein Struct akzeptiert, lautet:
void functionName(struct StructureName *ptr) {
// Funktionskörper unter Verwendung von ptr->member
}Innerhalb der Funktion verwenden Sie den Pfeiloperator, um auf die Struct-Member zuzugreifen und diese zu ändern. Alle über den Pointer vorgenommenen Änderungen wirken sich direkt auf das ursprüngliche Struct in der aufrufenden Funktion aus, was diesen Ansatz ideal für Funktionen macht, die Struct-Daten aktualisieren oder transformieren müssen.
Diese Technik ist grundlegend für die Erstellung effizienter C-Programme, die mit komplexen Datenstrukturen arbeiten, da sie die Leistungsfähigkeit von Zeigern mit der Organisation von Structs kombiniert.
Aufgabe
EinfachErstellen Sie ein C-Programm, das die Übergabe von Zeigern auf Structs an Funktionen zur effizienten Datenänderung demonstriert. Ihr Programm sollte:
- Definieren Sie ein
structnamensBankAccountmit den folgenden Mitgliedern:- Ein Integer
accountNumberzum Speichern der Kontonummer - Ein Zeichen-Array
ownerNamemit der Größe 30 zum Speichern des Namens des Kontoinhabers - Ein Float
balancezum Speichern des Kontostands - Ein Integer
transactionCountzum Speichern der Anzahl der Transaktionen
- Ein Integer
- Schreiben Sie eine Funktion namens
depositMoney, die:- Einen Zeiger auf ein
BankAccount-Struct und einen Float-Betrag als Parameter entgegennimmt - Den Betrag zum Kontostand unter Verwendung des Pfeil-Operators addiert
- Den Transaktionszähler um 1 erhöht
Deposit successful. New balance: [balance]ausgibt
- Einen Zeiger auf ein
- Schreiben Sie eine Funktion namens
withdrawMoney, die:- Einen Zeiger auf ein
BankAccount-Struct und einen Float-Betrag als Parameter entgegennimmt - Prüft, ob der Auszahlungsbetrag größer als der aktuelle Kontostand ist
- Bei unzureichender Deckung
Insufficient funds. Current balance: [balance]ausgibt - Bei ausreichender Deckung den Betrag vom Kontostand subtrahiert, den Transaktionszähler erhöht und
Withdrawal successful. New balance: [balance]ausgibt
- Einen Zeiger auf ein
- Schreiben Sie eine Funktion namens
displayAccount, die:- Einen Zeiger auf ein
BankAccount-Struct als Parameter entgegennimmt - Die Kontoinformationen in genau diesem Format ausgibt:
Account Information:Account Number: [accountNumber]Owner: [ownerName]Balance: [balance]Transactions: [transactionCount]
- Einen Zeiger auf ein
- In der main-Funktion:
- Erstellen Sie eine
BankAccount-Variable namensaccount - Lesen Sie die Kontonummer, den Namen des Inhabers und den Anfangskontostand von der Eingabe ein
- Setzen Sie den Transaktionszähler auf 0
- Rufen Sie
displayAccountauf, um den anfänglichen Kontostand anzuzeigen - Lesen Sie einen Einzahlungsbetrag ein und rufen Sie
depositMoneyauf - Lesen Sie einen Auszahlungsbetrag ein und rufen Sie
withdrawMoneyauf - Rufen Sie
displayAccountauf, um den endgültigen Kontostand anzuzeigen
- Erstellen Sie eine
Diese Herausforderung demonstriert die Leistungsfähigkeit der Übergabe von Zeigern auf Structs an Funktionen. Im Gegensatz zum Pass-by-Value erhalten die Funktionen die Speicheradresse des ursprünglichen Structs, was es ihnen ermöglicht, die ursprünglichen Daten direkt zu ändern. Dieser Ansatz ist effizienter (kein Kopieren) und ermöglicht es Funktionen, Struct-Werte in der aufrufenden Funktion dauerhaft zu ändern.
Spickzettel
Beim Übergeben von Strukturen als Wert (by value) wird die gesamte Struktur kopiert, was bei großen Strukturen ineffizient ist und das Ändern der Originaldaten verhindert. Das Übergeben von Zeigern auf Strukturen löst beide Probleme, indem stattdessen die Speicheradresse übergeben wird.
Funktionssyntax für die Annahme eines Zeigers auf eine Struktur:
void functionName(struct StructureName *ptr) {
// Funktionskörper unter Verwendung von ptr->member
}Verwenden Sie innerhalb der Funktion den Pfeiloperator (->), um über den Zeiger auf Strukturmitglieder zuzugreifen und diese zu ändern. Alle vorgenommenen Änderungen wirken sich direkt auf die ursprüngliche Struktur in der aufrufenden Funktion aus.
Dieser Ansatz bietet zwei wesentliche Vorteile:
- Effizienter (kein Kopieren der gesamten Struktur)
- Ermöglicht Funktionen, die ursprünglichen Strukturdaten zu ändern
Probier es selbst
#include <stdio.h>
#include <string.h>
// TODO: Definiere hier das BankAccount-Struct
// TODO: Schreibe hier die depositMoney-Funktion
// TODO: Schreibe hier die withdrawMoney-Funktion
// TODO: Schreibe hier die displayAccount-Funktion
int main() {
// Eingabe lesen
int accountNum;
char ownerName[30];
float initialBalance;
float depositAmount;
float withdrawAmount;
scanf("%d", &accountNum);
scanf("%s", ownerName);
scanf("%f", &initialBalance);
scanf("%f", &depositAmount);
scanf("%f", &withdrawAmount);
// TODO: BankAccount-Variable erstellen und initialisieren
// TODO: displayAccount aufrufen, um den Anfangszustand anzuzeigen
// TODO: depositMoney mit dem Einzahlungsbetrag aufrufen
// TODO: withdrawMoney mit dem Auszahlungsbetrag aufrufen
// TODO: displayAccount aufrufen, um den Endzustand anzuzeigen
return 0;
}Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Logic & Flow
1Zeiger-Grundlagen
Was ist ein Zeiger?Zeiger deklarierenDer Adressoperator (&)Der Dereferenzierungsoperator (*)NULL-ZeigerZusammenfassung: Zeiger-Grundlagen4Projekt: Einfaches Text-Tool
ProjektübersichtZeichen zählen2Pointer und Arrays
Array-Namen als PointerArray-Elemente - PointerPointer-ArithmetikPointer vergleichenWiederholung: Array-Durchlauf mit Pointern5Zeiger und Funktionen
Wertübergabe (Pass-by-Value)Zeiger an Funktionen übergebenVariablen über Zeiger ändernEin klassisches Beispiel: SwapArrays an Funktionen übergebenZusammenfassung: Zeiger als Funktionsargumente8Structs und Zeiger
Zeiger auf StructsDer Pfeiloperator (->)Structs per Wert übergebenÜbergabe von Struct-ZeigernDynamische Allokation von StructsZusammenfassung: Struct ändern - Ptr11Finale Wiederholungsaufgaben
Wiederholung: Dynamische String-KonkatenationWiederholung: Array von StructsWiederholung: Wortfrequenz-Zähler3Character-Arrays und Strings
Strings als char-ArraysDer Null-TerminatorString-Eingabe mit scanfVerwendung von strlen()Verwendung von strcpy()Verwendung von strcat()Verwendung von strcmp()Zusammenfassung: Grundlegende String-Funktionen