Menu
Coddy logo textTech

Die First-Member-Regel

Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 29 von 61.

Wenn Sie ein struct als erstes Mitglied eines anderen struct einbetten, passiert etwas Mächtiges: Die Speicheradresse des äußeren struct ist identisch mit der Speicheradresse seines ersten Mitglieds. Dies wird durch den C-Standard garantiert.

Betrachten wir unser vorheriges Beispiel:

typedef struct {
    int id;
} Parent;

typedef struct {
    Parent parent;  // Erstes Element
    int grade;
} Child;

Wenn Sie eine Child-Variable erstellen, befindet sich das Parent-Member ganz am Anfang des Speichers von Child. Das bedeutet, dass ein Zeiger auf Child auf genau dieselbe Adresse zeigt wie ein Zeiger auf sein Parent-Member.

Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr;  // Sicherer Cast!

Beide Pointer halten dieselbe Adresse. Dies ermöglicht es Ihnen, einen Child* an jede Funktion zu übergeben, die einen Parent* erwartet:

void print_id(Parent* p) {
    printf("ID: %d\n", p->id);
}

// In main:
Child c;
c.parent.id = 42;
print_id((Parent*)&c);  // Funktioniert einwandfrei

Diese Technik ist die Grundlage der Vererbung in C. Eine für den Basistyp geschriebene Funktion kann mit jedem abgeleiteten Typ arbeiten, solange die Basis zuerst eingebettet ist. Der Cast ist sicher, da die Speicherlayouts perfekt aufeinander abgestimmt sind.

challenge icon

Aufgabe

Einfach

Lassen Sie uns die "First Member Rule" (Regel des ersten Elements) untersuchen, indem wir eine einfache Tierhierarchie aufbauen. Sie werden eine Basis-Struktur Animal und eine abgeleitete Struktur Dog erstellen und dann demonstrieren, wie ein Zeiger auf Dog sicher in einen Zeiger auf Animal umgewandelt werden kann.

Die entscheidende Erkenntnis, die Sie anwenden werden: Wenn Animal das erste Mitglied von Dog ist, teilen sich beide Strukturen dieselbe Startadresse im Speicher. Das bedeutet, dass Sie einen Dog* an jede Funktion übergeben können, die einen Animal* erwartet, indem Sie den Zeiger casten.

Sie werden Ihren Code auf drei Dateien verteilen:

  • animal.h: Definieren Sie beide Strukturen mit Include-Guards. Ihre Animal-Struktur sollte ein einzelnes Feld haben: legs (ein Integer, der die Anzahl der Beine darstellt). Ihre Dog-Struktur sollte Animal als ihr erstes Mitglied einbetten und ein Feld name (ein Zeichen-Array von 50 Zeichen) hinzufügen. Deklarieren Sie außerdem eine Funktion print_animal, die einen Animal*-Parameter entgegennimmt.
  • animal.c: Implementieren Sie die Funktion print_animal. Diese Funktion arbeitet mit dem Basistyp – sie kennt nur Animal und gibt aus, wie viele Beine das Tier hat.
  • main.c: Erstellen Sie eine Dog-Variable und füllen Sie deren Felder (sowohl die Beine des eingebetteten Tieres als auch den Namen des Hundes). Demonstrieren Sie dann die "First Member Rule", indem Sie Ihren Dog* in einen Animal* casten und an print_animal übergeben. Geben Sie außerdem den Namen des Hundes separat aus, um das Gesamtbild zu zeigen.

Sie erhalten zwei Eingaben: den Namen des Hundes (einen String) und die Anzahl der Beine (einen Integer).

Ihre Ausgabe sollte wie folgt aussehen:

Dog: Buddy
Legs: 4

Wobei Buddy der Name des Hundes und 4 die Anzahl der Beine ist. Geben Sie zuerst den Namen des Hundes aus main.c aus und rufen Sie dann print_animal mit dem gecasteten Zeiger auf, um die Beine anzuzeigen.

Dies demonstriert, wie eine für den Basistyp (Animal) geschriebene Funktion nahtlos mit einem abgeleiteten Typ (Dog) durch Pointer-Casting zusammenarbeiten kann – die Grundlage der Vererbung in C.

Spickzettel

Wenn ein Struct als erstes Element in ein anderes Struct eingebettet ist, teilen sich beide Structs dieselbe Speicheradresse am Anfang. Dies wird durch den C-Standard garantiert.

typedef struct {
    int id;
} Parent;

typedef struct {
    Parent parent;  // First member
    int grade;
} Child;

Ein Pointer auf das äußere Struct kann sicher in einen Pointer auf sein erstes Element umgewandelt (gecastet) werden:

Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr;  // Safe cast - same address

Dies ermöglicht es, einen Pointer eines abgeleiteten Typs an Funktionen zu übergeben, die den Basistyp erwarten:

void print_id(Parent* p) {
    printf("ID: %d\n", p->id);
}

Child c;
c.parent.id = 42;
print_id((Parent*)&c);  // Works because memory layouts align

Diese Technik ist die Grundlage für Vererbung in C und ermöglicht es, dass Funktionen, die für Basistypen geschrieben wurden, auch mit abgeleiteten Typen arbeiten können.

Probier es selbst

#include <stdio.h>
#include <string.h>
#include "animal.h"

int main() {
    // Eingabe lesen
    char name[50];
    int legs;
    scanf("%s", name);
    scanf("%d", &legs);
    
    // TODO: Eine Dog Variable erstellen
    
    // TODO: Den Namen des Hundes mit strcpy setzen
    
    // TODO: Die Anzahl der Beine setzen (Zugriff über das eingebettete Animal)
    
    // TODO: Den Namen des Hundes im Format "Dog: <name>" ausgeben
    
    // TODO: Dog* zu Animal* casten und print_animal aufrufen
    // Dies demonstriert die "First Member Rule"!
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Object Oriented Programming