Menu
Coddy logo textTech

La règle du premier membre

Fait partie de la section Object Oriented Programming du Journey C de Coddy — leçon 29 sur 61.

Lorsque vous imbriquez une struct en tant que premier membre d'une autre struct, quelque chose de puissant se produit : l'adresse mémoire de la struct externe est identique à l'adresse mémoire de son premier membre. Ceci est garanti par la norme C.

Considérons notre exemple précédent :

typedef struct {
    int id;
} Parent;

typedef struct {
    Parent parent;  // Premier membre
    int grade;
} Child;

Lorsque vous créez une variable Child, le membre Parent se situe au tout début de la mémoire du Child. Cela signifie qu'un pointeur vers Child pointe vers la même adresse exacte qu'un pointeur vers son membre Parent.

Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr;  // Cast sécurisé !

Les deux pointeurs contiennent la même adresse. Cela vous permet de passer un Child* à n'importe quelle fonction attendant un Parent* :

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

// Dans main :
Child c;
c.parent.id = 42;
print_id((Parent*)&c);  // Fonctionne parfaitement

Cette technique est le fondement de l'héritage en C. Une fonction écrite pour le type de base peut opérer sur n'importe quel type dérivé, tant que la base est incorporée en premier. Le transtypage est sûr car les agencements de mémoire s'alignent parfaitement.

challenge icon

Défi

Facile

Explorons la règle du premier membre en construisant une hiérarchie animale simple. Vous allez créer une structure de base Animal et une structure dérivée Dog, puis démontrer comment un pointeur vers Dog peut être converti en toute sécurité en un pointeur vers Animal.

L'idée clé que vous allez appliquer : lorsque Animal est le premier membre de Dog, les deux structures partagent la même adresse mémoire de départ. Cela signifie que vous pouvez passer un Dog* à n'importe quelle fonction attendant un Animal* en convertissant le pointeur.

Vous organiserez votre code sur trois fichiers :

  • animal.h : Définissez les deux structures avec des gardes d'inclusion. Votre structure Animal doit avoir un seul champ : legs (un entier représentant le nombre de pattes). Votre structure Dog doit intégrer Animal comme son premier membre et ajouter un champ name (un tableau de caractères de 50 caractères). Déclarez également une fonction print_animal qui prend un paramètre Animal*.
  • animal.c : Implémentez la fonction print_animal. Cette fonction travaille avec le type de base — elle ne connaît que Animal et affiche le nombre de pattes de l'animal.
  • main.c : Créez une variable Dog et remplissez ses champs (à la fois les pattes de l'animal intégré et le nom du chien). Démontrez ensuite la règle du premier membre en convertissant votre Dog* en un Animal* et en le passant à print_animal. Affichez également le nom du chien séparément pour montrer l'ensemble complet.

Vous recevrez deux entrées : le nom du chien (une chaîne de caractères) et le nombre de pattes (un entier).

Votre sortie devrait ressembler à ceci :

Dog: Buddy
Legs: 4

Buddy est le nom du chien et 4 est le nombre de pattes. Affichez d'abord le nom du chien depuis main.c, puis appelez print_animal avec le pointeur converti pour afficher les pattes.

Cela démontre comment une fonction écrite pour le type de base (Animal) peut fonctionner de manière transparente avec un type dérivé (Dog) grâce à la conversion de pointeur — le fondement de l'héritage en C.

Aide-mémoire

Lorsqu'une structure est imbriquée en tant que premier membre d'une autre structure, les deux structures partagent la même adresse mémoire de départ. Ceci est garanti par la norme C.

typedef struct {
    int id;
} Parent;

typedef struct {
    Parent parent;  // Premier membre
    int grade;
} Child;

Un pointeur vers la structure externe peut être converti en toute sécurité en un pointeur vers son premier membre :

Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr;  // Cast sécurisé - même adresse

Cela permet de passer un pointeur de type dérivé à des fonctions attendant le type de base :

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

Child c;
c.parent.id = 42;
print_id((Parent*)&c);  // Fonctionne car les dispositions mémoire s'alignent

Cette technique est le fondement de l'héritage en C, permettant aux fonctions écrites pour les types de base d'opérer sur les types dérivés.

Essayez vous-même

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

int main() {
    // Lire l'entrée
    char name[50];
    int legs;
    scanf("%s", name);
    scanf("%d", &legs);
    
    // TODO: Créer une variable Dog
    
    // TODO: Définir le nom du chien en utilisant strcpy
    
    // TODO: Définir le nombre de pattes (accès via l'Animal incorporé)
    
    // TODO: Afficher le nom du chien au format : "Dog: <name>"
    
    // TODO: Caster Dog* en Animal* et appeler print_animal
    // Ceci démontre la règle du premier membre !
    
    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Object Oriented Programming