Zasada pierwszego elementu
Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 29 z 61.
Gdy osadzasz strukturę jako pierwszy element innej struktury, dzieje się coś niezwykłego: adres pamięci zewnętrznej struktury jest identyczny z adresem pamięci jej pierwszego elementu. Gwarantuje to standard C.
Rozważmy nasz poprzedni przykład:
typedef struct {
int id;
} Parent;
typedef struct {
Parent parent; // Pierwsze pole
int grade;
} Child;Gdy tworzysz zmienną Child, składowa Parent znajduje się na samym początku pamięci obiektu Child. Oznacza to, że wskaźnik do Child wskazuje dokładnie ten sam adres co wskaźnik do jego składowej Parent.
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // Bezpieczne rzutowanie!Oba wskaźniki przechowują ten sam adres. Dzięki temu możesz przekazać Child* do dowolnej funkcji oczekującej Parent*:
void print_id(Parent* p) {
printf("ID: %d\n", p->id);
}
// W main:
Child c;
c.parent.id = 42;
print_id((Parent*)&c); // Działa doskonaleTa technika stanowi podstawę dziedziczenia w C. Funkcja napisana dla typu bazowego może działać na dowolnym typie pochodnym, o ile typ bazowy jest osadzony jako pierwszy. Rzutowanie jest bezpieczne, ponieważ układy pamięci są idealnie zgodne.
Wyzwanie
ŁatwyPoznajmy zasadę pierwszego pola, tworząc prostą hierarchię zwierząt. Utworzysz bazową strukturę Animal i pochodną strukturę Dog, a następnie pokażesz, jak bezpiecznie rzutować wskaźnik do Dog na wskaźnik do Animal.
Najważniejsza kwestia: gdy Animal jest pierwszym polem struktury Dog, obie struktury mają ten sam początkowy adres w pamięci. Oznacza to, że możesz przekazać Dog* do dowolnej funkcji oczekującej Animal*, rzutując wskaźnik.
Rozmieścisz kod w trzech plikach:
animal.h: Zdefiniuj obie struktury z osłonami przed wielokrotnym dołączaniem. StrukturaAnimalpowinna mieć jedno pole:legs(liczbę całkowitą określającą liczbę nóg). StrukturaDogpowinna zawieraćAnimaljako pierwsze pole i dodatkowe polename(tablicę znaków o długości 50 znaków). Zadeklaruj także funkcjęprint_animal, która przyjmuje parametrAnimal*.animal.c: Zaimplementuj funkcjęprint_animal. Ta funkcja działa z typem bazowym. Wie tylko oAnimali wypisuje, ile nóg ma zwierzę.main.c: Utwórz zmienną typuDogi wypełnij jej pola (zarówno liczbę nóg osadzonego zwierzęcia, jak i imię psa). Następnie zademonstruj zasadę pierwszego pola, rzutując wskaźnikDog*naAnimal*i przekazując go doprint_animal. Wypisz też osobno imię psa, aby pokazać pełny obraz.
Otrzymasz dwa dane wejściowe: imię psa (ciąg znaków) oraz liczbę nóg (liczbę całkowitą).
Wynik powinien wyglądać tak:
Dog: Buddy
Legs: 4W tym przykładzie Buddy to imię psa, a 4 to liczba jego nóg. Najpierw wypisz imię psa z main.c, a następnie wywołaj print_animal z rzutowanym wskaźnikiem, aby wyświetlić liczbę nóg.
To pokazuje, jak funkcja napisana dla typu bazowego (Animal) może bezproblemowo działać z typem pochodnym (Dog) dzięki rzutowaniu wskaźników: to podstawa dziedziczenia w C.
Spróbuj swoich sił
#include <stdio.h>
#include <string.h>
#include "animal.h"
int main() {
// Wczytaj dane wejściowe
char name[50];
int legs;
scanf("%s", name);
scanf("%d", &legs);
// TODO: Utwórz zmienną typu Dog
// TODO: Ustaw imię psa za pomocą strcpy
// TODO: Ustaw liczbę nóg (uzyskaj dostęp przez osadzony Animal)
// TODO: Wypisz imię psa w formacie: "Dog: <name>"
// TODO: Rzutuj Dog* na Animal* i wywołaj print_animal
// To pokazuje zasadę pierwszego pola!
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania modularnego
Pliki nagłówkoweZabezpieczenia przed wielokrotnym dołączeniemPliki źródłoweFunkcje statycznePodsumowanie: kalkulator modularny4Enkapsulacja
Koncepcja niejawnych wskaźnikówDefiniowanie niejawnych strukturGettery i setteryWalidacja w setterachPowtórka: tajemnicze pudełko2Obiekty i metody
Struktury jako obiektyWskaźnik „self”Poprawność constWskaźnik czy wartośćMetody pomocniczePodsumowanie: menedżer punktów5Projekt: Proste konto bankowe
Konfiguracja projektuImplementacja konta8Polimorfizm
Wskaźniki do funkcji w strukturachSymulowanie metodKoncepcja interfejsuImplementowanie interfejsówIteracja polimorficznaPodsumowanie: Greeter11Wzorce projektowe w C
Wzorzec SingletonWzorzec fabrykiWzorzec iteratoraPodsumowanie: fabryka loggera3Cykl życia obiektu
Wzorzec konstruktoraWzorzec destruktoraInicjalizacja na stosieKopia głębokaPodsumowanie: klasa opakowująca ciąg znaków6Dziedziczenie przez kompozycję
Osadzanie strukturZasada pierwszego elementuDostęp do elementów klasy nadrzędnejKonwersja w górę hierarchiiPodsumowanie: hierarchia kształtów9Projekt: Rysownik kształtów
Przegląd projektuImplementacja kołaImplementacja prostokątaZastosowanie polimorfizmuKontener kształtówPoćwicz samodzielnie: Kompilator C online