← HUB ✦ Holberton School — C Programming ✦

STRUCTURES
& TYPEDEF

Crée tes propres types. Domine la mémoire.

C'EST QUOI UNE STRUCTURE ?
🧱 DÉFINITION

Une structure est un type de données composite défini par l'utilisateur. Elle regroupe des variables de types différents sous un même identifiant. C'est le seul moyen en C de créer un type "objet" contenant plusieurs champs.

🕹️ ANALOGIE GAMING

Dans un RPG, chaque personnage a un nom, des points de vie, un niveau, une arme. Toutes ces infos forment une fiche de personnage. En C, une struct, c'est exactement cette fiche : un seul "objet" qui regroupe toutes les données liées à une entité. Au lieu de jongler avec 10 variables séparées, tu as une seule struct Player.

Une struct occupe un bloc contigu en mémoire. Chaque champ est à un offset fixe depuis le début de la struct.
DÉCLARER UNE STRUCTURE
📐 SYNTAXE DE BASE
1

Définir la structure (scope global, avant main)

/* Définition du type — scope global */
struct User
{
    char  *name;   /* offset 0  — pointeur (8 bytes) */
    char  *email;  /* offset 8  — pointeur (8 bytes) */
    int   age;     /* offset 16 — int (4 bytes)      */
};               /* ← NE PAS oublier le ; final !  */
2

Déclarer une variable de ce type

int main(void)
{
    struct User user;       /* variable sur la stack */
    struct User *ptr;       /* pointeur vers struct  */
    return (0);
}
3

Accéder aux champs avec .

user.name  = "Foo Bar";
user.email = "[email protected]";
user.age   = 98;

printf("%s — %d ans\n", user.name, user.age);
LA STRUCTURE EN MÉMOIRE
🗺️ LAYOUT MÉMOIRE — struct User

Une struct occupe un bloc contigu. Les champs sont placés dans l'ordre de déclaration, avec possible padding pour l'alignement.

struct User — sizeof = 24 bytes (sur système 64-bit)
+0 (8B)
PTR name — adresse de la chaîne "Foo Bar"
+8 (8B)
PTR email — adresse de la chaîne "[email protected]"
+16 (4B)
INT age — valeur 98 directement stockée
+20 (4B)
padding — alignement mémoire (ajouté par le compilateur)
sizeof(struct User) donne la taille totale incluant le padding. Toujours utiliser sizeof avec malloc, jamais calculer manuellement !
Ordonne tes champs du plus grand au plus petit pour minimiser le padding et économiser de la mémoire.
POINTEURS VERS UNE STRUCTURE — . vs ->
🎯 L'OPÉRATEUR FLÈCHE ->

Quand tu travailles avec un pointeur vers une struct, tu dois déréférencer avant d'accéder au champ. -> fait les deux d'un coup.

❌ VERBOSE (mais correct)
struct User *ptr = &user;

/* Déréférence + accès */
(*ptr).name = "Foo";
(*ptr).age  = 98;
✅ ÉLÉGANT (Holberton style)
struct User *ptr = &user;

/* Flèche = déréférence + accès */
ptr->name = "Foo";
ptr->age  = 98;
🕹️ ANALOGIE GAMING

. c'est comme avoir la fiche de personnage entre les mains (accès direct). -> c'est comme avoir l'adresse du coffre qui contient la fiche : la flèche ouvre le coffre ET lit la fiche en une seule action.

STRUCT + MALLOC — LE PATTERN HOLBERTON
🛠️ ALLOUER UNE STRUCT DYNAMIQUEMENT

Pattern classique des projets Holberton : une fonction new_XXX qui alloue et initialise une struct.

#include <stdlib.h>
#include <stdio.h>

struct User
{
    char *name;
    char *email;
    int  age;
};

struct User *new_user(char *name, char *email, int age)
{
    struct User *user;

    user = malloc(sizeof(struct User)); /* alloue la struct */
    if (user == NULL)
        return (NULL);                   /* toujours vérifier ! */
    user->name  = name;
    user->email = email;
    user->age   = age;
    return (user);
}

int main(void)
{
    struct User *u = new_user("Foo", "[email protected]", 98);

    if (u == NULL)
        return (1);
    printf("User %s created !\n", u->name);
    printf("His email is: %s\n", u->email);
    printf("And he is %d years old\n", u->age);
    free(u);        /* ne jamais oublier ! */
    return (0);
}
Toujours free() les structs allouées avec malloc. Si la struct contient des pointeurs vers d'autres malloc, libère-les d'abord !
TYPEDEF — RENOMME TES TYPES
✏️ DÉFINITION

Le mot-clé typedef permet de donner un alias à un type existant. Il ne crée pas un nouveau type — il crée un nouveau nom pour un type déjà existant.

🕹️ ANALOGIE GAMING

Dans ton jeu, une épée longue s'appelle techniquement weapon_type_melee_blade_long. Avec typedef, tu lui donnes l'alias Sword. C'est le même objet, même comportement — juste un nom plus court et lisible pour ton code.

SYNTAXES TYPEDEF — DU SIMPLE AU COMPLEXE
📋 CAS 1 — TYPES SIMPLES

Donner un alias à un type primitif pour la lisibilité ou la portabilité.

/* Syntaxe : typedef <type_original> <nouveau_nom>; */

typedef unsigned char  byte;   /* byte = unsigned char */
typedef unsigned int   uint;   /* uint = unsigned int  */
typedef char          *string; /* string = char*       */

/* Utilisation : exactement comme le type original */
byte c = 200;
uint i = 42;
string name = "Holberton";
🧱 CAS 2 — TYPEDEF + STRUCT (le plus courant à Holberton)

Évite de réécrire struct partout. Deux syntaxes équivalentes :

MÉTHODE A — en 2 étapes
struct Computer {
    char *name;
    char *brand;
};
typedef struct Computer Computer;

/* Utilisation */
Computer pc;  /* au lieu de struct Computer pc */
MÉTHODE B — en 1 étape ✅
typedef struct Computer {
    char *name;
    char *brand;
} Computer;

/* Utilisation */
Computer pc;  /* propre et direct */
La Méthode B est la plus utilisée à Holberton. Elle combine la définition de la struct et son alias en une seule déclaration.
Garde le même nom pour le tag struct et le typedef (ex: struct User → User). Certains style guides interdisent les typedef de struct — Holberton les autorise et les encourage avec Betty.
⚡ CAS 3 — TYPEDEF + FUNCTION POINTER (rappel)

Indispensable pour les projets _printf et Simple Shell :

/* Sans typedef — illisible */
int (*op)(int, int);

/* Avec typedef — clair et réutilisable */
typedef int (*op_t)(int, int);

op_t add_op;
op_t ops[10];   /* tableau de 10 function pointers */

/* Combo struct + function pointer — pattern _printf */
typedef struct spec_s {
    char *specifier;
    int  (*f)(va_list);
} spec_t;
TABLEAU RÉCAP — TOUT EN UN COUP D'ŒIL
📊 CHEAT TABLE STRUCTURES
SyntaxeCe que ça faitExemple
struct Nom { ... }; Déclare un nouveau type struct struct User { int age; };
struct Nom var; Crée une variable de ce type struct User u;
var.champ Accède à un champ (variable directe) u.age = 98;
ptr->champ Accède à un champ (via pointeur) ptr->age = 98;
sizeof(struct Nom) Taille en bytes de la struct malloc(sizeof(struct User))
typedef struct Nom { } Alias; Struct + alias en une fois typedef struct User { } User;
Alias var; Déclare sans mot-clé struct User u;
LES 3 BOSS — EXEMPLES PROGRESSIFS
🐣 BOSS 1 — STRUCT SIMPLE AVEC TYPEDEF
⚔️ LVL 1 — FACILE
#include <stdio.h>

typedef struct point_s {
    int x;
    int y;
} point_t;

int main(void)
{
    point_t p;         /* plus besoin de "struct" */

    p.x = 10;
    p.y = 42;
    printf("Point: (%d, %d)\n", p.x, p.y);
    return (0);
}
⚔️ BOSS 2 — STRUCT IMBRIQUÉE + POINTEUR
🔥 LVL 2 — INTERMÉDIAIRE
#include <stdio.h>

typedef struct date_s {
    int day;
    int month;
    int year;
} date_t;

typedef struct player_s {
    char   *name;
    int    level;
    date_t birthday;    /* struct imbriquée */
} player_t;

void print_player(player_t *p)  /* toujours passer par pointeur */
{
    printf("%s — LVL %d\n", p->name, p->level);
    printf("Né le %d/%d/%d\n",
        p->birthday.day,
        p->birthday.month,
        p->birthday.year);
}

int main(void)
{
    player_t hero = {"Link", 42, {1, 9, 2001}};

    print_player(&hero);
    return (0);
}
Passe toujours les structs en argument par pointeur, jamais par valeur — copier une struct entière est coûteux en mémoire !
💀 BOSS FINAL — LINKED LIST NODE (style Holberton)
💀 LVL 3 — HOLBERTON STYLE
#include <stdlib.h>
#include <stdio.h>

/* Struct qui se référence elle-même — forward déclaration */
typedef struct node_s {
    int          n;
    struct node_s *next;  /* pointeur vers le même type */
} node_t;

node_t *new_node(int n)
{
    node_t *node = malloc(sizeof(node_t));

    if (node == NULL)
        return (NULL);
    node->n    = n;
    node->next = NULL;
    return (node);
}

void print_list(node_t *head)
{
    while (head != NULL)
    {
        printf("[%d] -> ", head->n);
        head = head->next;
    }
    printf("NULL\n");
}

int main(void)
{
    node_t *head = new_node(1);

    head->next       = new_node(2);
    head->next->next = new_node(3);
    print_list(head);  /* [1] -> [2] -> [3] -> NULL */
    return (0);
}
Dans une struct auto-référencée, tu dois utiliser struct node_s *next (avec le tag complet), pas node_t *next — le typedef n'est pas encore connu à ce stade de la déclaration !
ERREURS CLASSIQUES — ❌ vs ✅
❌ MAUVAIS
/* Oublier le ; après la struct */
struct User { int age; }
/* → erreur de compilation */

/* . sur un pointeur */
struct User *p = &u;
p.age = 98;  /* ❌ p est un pointeur ! */

/* -> sur une variable directe */
struct User u;
u->age = 98; /* ❌ u n'est pas un pointeur ! */

/* Oublier de vérifier malloc */
p = malloc(sizeof(struct User));
p->age = 42; /* ❌ si malloc échoue → segfault */
✅ CORRECT
/* ; obligatoire ! */
struct User { int age; };
/* ✅ */

/* -> sur un pointeur */
struct User *p = &u;
p->age = 98; /* ✅ */

/* . sur une variable directe */
struct User u;
u.age = 98;  /* ✅ */

/* Vérifier malloc */
p = malloc(sizeof(struct User));
if (!p) return (NULL);
p->age = 42; /* ✅ */
TIPS SPÉCIAUX HOLBERTON
🎓 CONVENTIONS BETTY + HOLBERTON
Nomme tes types avec le suffixe _t

Convention Holberton : typedef struct user_s { ... } user_t;. Le tag interne prend _s, l'alias prend _t.

Déclare tes structs dans le header (.h)

Jamais dans un .c — les structs partagées entre fichiers doivent être dans les headers avec les header guards.

/* main.h */
#ifndef MAIN_H
#define MAIN_H

typedef struct user_s {
    char *name;
    int  age;
} user_t;

#endif
Valgrind — libère les champs avant la struct

Si une struct contient des pointeurs mallocés, libère d'abord les champs, puis la struct.

free(user->name);   /* 1. champs d'abord */
free(user->email);  /* 2. champs d'abord */
free(user);         /* 3. struct en dernier */
Projet _printf — le pattern spec_t

La combinaison struct + typedef + function pointer est au cœur du projet _printf : un tableau de spec_t associe chaque spécifier à sa fonction d'impression.

typedef struct spec_s {
    char *s;
    int  (*f)(va_list);
} spec_t;

spec_t specs[] = {
    {"d", print_int},
    {"s", print_str},
    {"c", print_char},
    {NULL, NULL}
};
RÉSUMÉ — TOUT EN UN COUP D'ŒIL
🗺️ LA CARTE MENTALE COMPLÈTE
/*
 *  STRUCT                          TYPEDEF
 *  ──────────────────────────────────────────────────────────
 *  Groupe des variables             Donne un alias à un type
 *  de types différents
 *
 *  struct User { ... };             typedef unsigned char byte;
 *  struct User u;                   typedef struct User { } User;
 *  u.name = "Foo";      (direct)
 *  ptr->name = "Foo";   (pointeur)
 *
 *  sizeof(struct User)  → taille    User u;  ← sans "struct" !
 *  malloc(sizeof(...))  → heap      user_t   ← convention _t
 *
 *  RÈGLES D'OR :
 *  ─────────────────────────────────────────────────────────
 *  1. Toujours ; après la définition de struct
 *  2.  .  si variable directe  |  ->  si pointeur
 *  3. Toujours vérifier NULL après malloc
 *  4. Libérer les champs AVANT la struct (valgrind !)
 *  5. Structs partagées → header avec header guards
 *  6. Convention Holberton : struct nom_s { } nom_t;
 */