Crée tes propres types. Domine la mémoire.
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.
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.
/* 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 ! */
int main(void)
{
struct User user; /* variable sur la stack */
struct User *ptr; /* pointeur vers struct */
return (0);
}
.user.name = "Foo Bar";
user.email = "[email protected]";
user.age = 98;
printf("%s — %d ans\n", user.name, user.age);
Une struct occupe un bloc contigu. Les champs sont placés dans l'ordre de déclaration, avec possible padding pour l'alignement.
sizeof(struct User) donne la taille totale incluant le padding. Toujours utiliser sizeof avec malloc, jamais calculer manuellement !->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.
struct User *ptr = &user;
/* Déréférence + accès */
(*ptr).name = "Foo";
(*ptr).age = 98;
struct User *ptr = &user;
/* Flèche = déréférence + accès */
ptr->name = "Foo";
ptr->age = 98;
. 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.
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);
}
free() les structs allouées avec malloc. Si la struct contient des pointeurs vers d'autres malloc, libère-les d'abord !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.
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.
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";
Évite de réécrire struct partout. Deux syntaxes équivalentes :
struct Computer {
char *name;
char *brand;
};
typedef struct Computer Computer;
/* Utilisation */
Computer pc; /* au lieu de struct Computer pc */
typedef struct Computer {
char *name;
char *brand;
} Computer;
/* Utilisation */
Computer pc; /* propre et direct */
struct User → User). Certains style guides interdisent les typedef de struct — Holberton les autorise et les encourage avec Betty.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;
| Syntaxe | Ce que ça fait | Exemple |
|---|---|---|
| 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; |
#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);
}
#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);
}
#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);
}
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 !/* 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 */
/* ; 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; /* ✅ */
_t
Convention Holberton : typedef struct user_s { ... } user_t;. Le tag interne prend _s, l'alias prend _t.
.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
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 */
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}
};
/*
* 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;
*/