• Aucun résultat trouvé

Chapitre 4. Les pointeurs et références

4.11. Allocation dynamique de mémoire

4.11.1. Allocation dynamique de mémoire en C

Il existe deux principales fonctions C permettant de demander de la mémoire au système d’exploitation et de la lui restituer. Elles utilisent toutes les deux les pointeurs, parce qu’une variable allouée dynamiquement n’a pas d’identificateur étant donné qu’elle n’était a priori pas connue à la compilation, et n’a donc pas pu être déclarée. Les pointeurs utilisés par ces fonctions C n’ont pas de type. On les référence donc avec des pointeurs non typés. Leur syntaxe est la suivante :

malloc(taille) free(pointeur)

malloc(abréviation de « Memory ALLOCation ») alloue de la mémoire. Elle attend comme para-mètre la taille de la zone de mémoire à allouer et renvoie un pointeur non typé (void *).

free(pour « FREE memory ») libère la mémoire allouée. Elle attend comme paramètre le pointeur sur la zone à libérer et ne renvoie rien.

Lorsqu’on alloue une variable typée, on doit faire un transtypage du pointeur renvoyé parmallocen pointeur de ce type de variable.

Pour utiliser les fonctionsmallocetfree, vous devez mettre au début de votre programme la ligne :

#include <stdlib.h>

Son rôle est similaire à celui de la ligne#include <stdio.h>. Vous verrez sa signification dans le chapitre concernant le préprocesseur.

L’exemple suivant va vous présenter un programme C classique qui manipule des pointeurs. Ce pro-gramme réalise des allocations dynamiques de mémoire et manipule une liste de structures dyna-miquement, en fonction des entrées que fait l’utilisateur. Les techniques de saisies de paramètres présentées dans le premier chapitre sont également revues. Ce programme vous présente aussi com-ment passer des paramètres par variable, soit pour optimiser le programme, soit pour les modifier au sein des fonctions appelées. Enfin, l’utilisation du mot clefconstavec les pointeurs est également illustrée.

Exemple 4-12. Allocation dynamique de mémoire en C

#include <stdio.h>

#include <stdlib.h> /* Fichier d’en-tête pour malloc et free. */

#include <string.h> /* Fichier d’en-tête pour strcpy, strlen et de strcmp. */

/* Type de base d’un élément de liste de personne. */

typedef struct person {

char *name; /* Nom de la personne. */

char *address; /* Adresse de la personne. */

struct person *next; /* Pointeur sur l’élément suivant. */

} Person;

typedef Person *People; /* Type de liste de personnes. */

/* Fonctions de gestion des listes de personnes : */

/* Fonction d’initialisation d’une liste de personne.

La liste est passée par variable pour permettre son initialisation. */

Chapitre 4. Les pointeurs et références void init_list(People *lst)

{

*lst = NULL;

}

/* Fonction d’ajout d’une personne. Les paramètres de la personne sont passés par variables, mais ne peuvent être modifiés car ils sont constants. Ce sont des chaînes de caractères C, qui sont donc assimilées à des pointeurs de caractères constants. */

int add_person(People *lst, const char *name, const char *address) {

/* Crée un nouvel élément : */

Person *p = (Person *) malloc(sizeof(Person));

if (p != NULL) {

/* Alloue la mémoire pour le nom et l’adresse. Attention, il faut compter le caractère nul terminal des chaînes : */

p->name = (char *) malloc((strlen(name) + 1) * sizeof(char));

p->address = (char *) malloc((strlen(address) + 1) * sizeof(char));

if (p->name != NULL && p->address != NULL) {

/* Fonction de suppression d’une personne.

La structure de la liste est modifiée par la suppression

de l’élément de cette personne. Cela peut impliquer la modification du chaînage de l’élément précédent, ou la modification de la tête de liste elle-même. */

int remove_person(People *lst, const char *name) {

/* Recherche la personne et son antécédant : */

Person *prev = NULL;

Person *p = *lst;

while (p != NULL) {

/* On sort si l’élément courant est la personne recherchée : */

if (strcmp(p->name, name) == 0) break;

Chapitre 4. Les pointeurs et références

/* Simple fonction d’affichage. */

void print_list(People const *lst) {

Person const *p = *lst;

int i = 1;

while (p != NULL) {

printf("Personne %d : %s (%s)\n", i, p->name, p->address);

p = p->next;

++i;

} }

/* Fonction de destruction et de libération de la mémoire. */

void destroy_list(People *lst)

Chapitre 4. Les pointeurs et références /* Utilise la liste : */

do {

printf("Opération (0 = quitter, 1 = ajouter, 2 = supprimer) ?");

fgets(buffer, 16, stdin);

fgets(name, 256, stdin); /* Lit le nom. */

name[255] = 0; /* Assure que le caractère nul terminal est écrit. */

s = strlen(name); /* Supprime l’éventuel saut de ligne. */

if (name[s - 1] == ’\n’) name[s - 1] = 0;

/* Même opération pour l’adresse : */

printf("Adresse : ");

if (remove_person(&p, name) == 0) {

if (op != 0) print_list(&p);

} while (op != 0);

/* Détruit la liste : */

destroy_list(&p);

return EXIT_SUCCESS;

}

Note : Comme vous pouvez le constater, la lecture des chaînes de caractères saisies par l’utilisateur est réalisée au moyen de la fonction fgets de la bibliothèque C standard. Cette fonction permet de lire une ligne complète sur le flux spécifié en troisième paramètre, et de stocker le résultat dans la chaîne de caractères fournie en premier paramètre. Elle ne lira pas plus de caractères que le nombre indiqué en deuxième paramètre, ce qui permet de contrôler la taille des lignes saisies par l’utilisateur. La fonctionfgetsnécessite malheureusement quelques traitements supplémentaires avant de pouvoir utiliser la chaîne de caractères lue, car elle n’écrit pas le caractère nul terminal de la chaîne C si le nombre maximal de caractères à lire est atteint,

Chapitre 4. Les pointeurs et références

et elle stocke le caractère de saut de ligne en fin de ligne si ce nombre n’est pas atteint. Il est donc nécessaire de s’assurer que la ligne se termine bien par un caractère nul terminal d’une part, et de supprimer le caractère de saut de ligne s’il n’est pas essentiel d’autre part.

Ces traitements constituent également un bon exemple de manipulation des pointeurs et des chaînes de caractères.

Ce programme n’interdit pas les définitions multiples de personnes ayant le même nom. Il n’interdit pas non plus la définition de personnes anonymes. Le lecteur pourra essayer de corriger ces petits défauts à titre d’exercice, afin de s’assurer que les notions de pointeur sont bien assimilées. Rappelons que les pointeurs sont une notion essentielle en C et qu’il faut être donc parfaitement familiarisé avec eux.