Files

183 lines
6.0 KiB
C
Raw Permalink Normal View History

2024-10-24 16:29:20 +02:00
/**
* @file alloc.h
* @brief Macros et fonctions d'allocation mémoire.
*
* @details Les macros MALLOC(), CALLOC() et REALLOC() définies dans ce fichier
* permettent d'allouer de la mémoire dynamiquement, en vérifiant que
* l'allocation a réussi. En cas d'échec, elles affichent un message d'erreur
* sur la sortie erreur, en indiquant la fonction, le fichier et la ligne
* l'allocation a échoué, et quittent le programme.
*
* La fonction multiple_free() permet de libérer plusieurs pointeurs en
* un seul appel (mais attention, aucun de ces pointeurs ne doit être `NULL`, et
* tous doivent avoir é alloués dynamiquement avant cette libération).
*
* La macro CHECK_NULL() permet de vérifier qu'un ou plusieurs pointeurs ne sont
* pas `NULL`.
*
* La fonction check_null() n'est pas destinée à être utilisée directement, mais
* est utilisée par la macro CHECK_NULL().
*/
#ifndef ALLOC_H_
#define ALLOC_H_
#include "error.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
/**
* @brief Macro pour allouer de la mémoire en utilisant la fonction malloc().
* @param p Pointeur à allouer.
* @param num_objects Nombre d'objets à allouer.
*
* @details
* Alloue de la mémoire permettant de stocker `num_objects`, chacun de type
* `*p`.
*
* **Exemple d'utilisation :**
* ```
* backmove *p;
* MALLOC(p, 10); // Alloue un tableau de 10 backmove.
* ```
*
* Si l'allocation échoue, affiche un message d'erreur et quitte le programme.
*
* @attention
* Le pointeur `p` doit avoir é déclaré, mais ne pas pointer vers une zone
* déjà allouée dynamiquement (sous peine de créer une fuite mémoire).
* Si `p` pointe déjà vers une zone allouée dynamiquement, il faut utiliser
* REALLOC.
*
* @see REALLOC()
*/
#define MALLOC(p, num_objects) \
do { \
void *tmp = malloc(sizeof *(p) * (num_objects)); \
if (!tmp) { \
FATAL("Malloc error."); \
} \
(p) = tmp; \
} while (0)
/**
* @brief Macro pour allouer de la mémoire en utilisant la fonction calloc().
* @param p Pointeur à allouer.
* @param num_objects Nombre d'objets à allouer.
*
* @details
* Alloue de la mémoire initialisée à 0, permettant de stocker `num_objects`,
* chacun de type `*p`.
*
* **Exemple d'utilisation :**
* ```
* backmove *p;
* CALLOC(p, 10); // Alloue un tableau de 10 backmove, initialisé à 0.
* ```
*
* Si l'allocation échoue, affiche un message d'erreur et quitte le programme.
*
* @attention
* Le pointeur `p` doit avoir é déclaré, mais ne pas pointer vers une zone
* déjà allouée dynamiquement (sous peine de créer une fuite mémoire).
* Si `p` pointe déjà vers une zone allouée dynamiquement, il faut utiliser
* REALLOC.
*
* @sa REALLOC()
*/
#define CALLOC(p, num_objects) \
do { \
void *tmp = calloc((num_objects), sizeof *(p)); \
if (!tmp) { \
FATAL("Calloc error."); \
} \
(p) = tmp; \
} \
while (0)
/**
* @brief Macro pour réallouer de la mémoire en utilisant la fonction realloc().
* @param p Pointeur à allouer.
* @param num_objects Nouveau nombre d'objets à allouer.
*
* -alloue la mémoire pointée par un pointeur p, dont la valeur doit avoir é
* retournée précédemment par une fonction d'allocation dynamique (malloc,
* calloc, realloc ou une des macros de ce fichier).
*
* Si l'allocation échoue, affiche un message d'erreur et quitte le programme.
*
* **Exemple d'utilisation :**
* ```
* backmove *p;
* MALLOC(p, 10); // Alloue un tableau de 10 backmove.
* // ...
* REALLOC(p, 20); // Ré-alloue un tableau de 20 backmove en copiant le tableau
* // précédent en début ce nouveay tableau, et affecte à p
* // l'adresse de la première case de ce tableau.
* ```
*
* Si l'allocation échoue, affiche un message d'erreur et quitte le programme.
*
* @attention
* Le pointeur `p` doit avoir é déclaré et pointer vers une zone
* déjà allouée dynamiquement.
*
* @sa MALLOC(), CALLOC()
*/
#define REALLOC(p, num_objects) \
do { \
void *tmp = realloc((p), sizeof *(p) * (num_objects)); \
if (!tmp) { \
FATAL("Realloc error."); \
} \
(p) = tmp; \
} while (0)
/**
* @brief Macro permettant de tester si un ou plusieurs pointeurs sont NULL.
* @param n Nombre de pointeurs à tester.
* @param ... Pointeurs à tester, en alternance avec des chaînes de caractères.
*
* Il doit y avoir un pointeur à tester pour chaque chaîne de caractères, et
* inversement.
*
* La chaîne de caractères qui suit chaque pointeur permet de personnaliser le
* message d'erreur.
*
* @details
* **Exemple d'utilisation :**
* ```
* CHECK_NULL(3, p1, "Name1", p2, "Name2", p3, "Name3");
* ```
* Ici, 3 est le nombre de pointeurs à vérifier, et p1, p2, p3 sont les
* pointeurs. Si `p1` et `p3` sont NULL, la macro affichera un message d'erreur
* de la forme :
***
[CRITICAL] Name1 is NULL!\n
[CRITICAL] Name3 is NULL!
***
* @sa check_null()
*/
#define CHECK_NULL(n, ...) \
check_null \
(__func__, __FILE__, __LINE__, \
n __VA_OPT__( , ) __VA_ARGS__)
/**
* @brief Ne pas utiliser cette fonction directement, mais à la place la macro
* ::CHECK_NULL(), qui indique dans le message la position de l'erreur dans le
* source.
*
* @sa CHECK_NULL()
*/
void check_null(const char *function, char *file, int line, int n, ...);
#endif // ALLOC_H_