Skip to content
Snippets Groups Projects
Forked from programmation_sequentielle / cours
118 commits behind the upstream repository.
title: "Allocation dynamique de mémoire"
date: "2022-11-22"

Allocation dynamique de mémoire (1/9)

  • La fonction malloc{.C} permet d'allouer dynamiquement (pendant l'exécution du programme) une zone de mémoire contiguë.

    #include <stdlib.h>
    void *malloc(size_t size);
  • size{.C} est la taille de la zone mémoire en octets.

  • Retourne un pointeur sur la zone mémoire ou NULL{.C} en cas d'échec: toujours vérifier que la valeur retournée est != NULL{.C}.

  • Le type du retour est void *{.C} (un pointeur de type quelconque).

Allocation dynamique de mémoire (2/9)

  • On peut allouer et initialiser une fraction_t{.C}:

    fraction_t *num = malloc(sizeof(fraction_t));
    num->num = 1;
    num->denom = -1;
  • La zone mémoire n'est pas initialisée.

  • Désallouer la mémoire explicitement

    \Rightarrow
    fuites mémoires.

  • Il faut connaître la taille des données à allouer.

La représentation mémoire de fraction_t et fuites.

Allocation dynamique de mémoire (3/9)

  • La fonction free(){.C} permet de libérer une zone préalablement allouée avec malloc(){.C}.

    #include <stdlib.h>
    void free(void *ptr);
  • Pour chaque malloc(){.C} doit correspondre exactement un free(){.C}.

  • Si la mémoire n'est pas libérée: fuite mémoire (l'ordinateur plante quand il y a plus de mémoire).

  • Si la mémoire est libérée deux fois: seg. fault.

  • Pour éviter les mauvaises surprises mettre ptr{.C} à NULL{.C} après libération.

Allocation dynamique de mémoire (4/9)

Tableaux dynamiques

  • Pour allouer un espace mémoire de 50 entiers: un tableau

    int *p = malloc(50 * sizeof(int));
    for (int i = 0; i < 50; ++i) {
        p[i] = 0;
    }

Arithmétique de pointeurs

  • Parcourir la mémoire différemment qu'avec l'indexation

    int *p = malloc(50 * sizeof(int));
    // initialize somehow
    double a = p[7];
    double b = *(p + 7); // on avance de 7 "double"
    p[0] == *p; // le pointeur est le premier élément

Allocation dynamique de mémoire (5/9)

Arithmétique de pointeurs

L'arithmétique des pointeurs.

Quelle est la complexité de l'accès à une case d'un tableau?

. . .

O(1). \mathcal{O}(1).

Allocation dynamique de mémoire (6/9)

Questions

int *p = malloc(50 * sizeof(int));
for (int i = 0; i < 50; ++i)
    p[i] = 0;   // ça fait quoi?
sizeof(p);      // ça vaut quoi?
sizeof(*p);     // ça vaut quoi?
p[10]     = -1; // ça fait quoi?
(p + 20)  = -1; // ça fait quoi?
*(p + 20) = -1; // ça fait quoi?
p[-1]     = -1; // ça fait quoi?    
p[50]     = -1; // ça fait quoi?    
7[p]      = 12; // ça fait quoi?

Allocation dynamique de mémoire (7/9)

Pointeur de pointeur

  • Tout comme une valeur a une adresse, un pointeur a lui-même une adresse:

    int a = 2;
    int *b = &a;
    int **c = &b;
  • Chaque *{.C} ou &{.C} rajoute une indirection.

Allocation dynamique de mémoire (8/9)

Pointeur de pointeur

Les références de pointeurs.

Allocation dynamique de mémoire (9/9)

  • Avec malloc(), on peut allouer dynamiquement des tableaux de pointeurs:

    int **p = malloc(50 * sizeof(int*));
    for (int i = 0; i < 50; ++i) {
        p[i] = malloc(70 * sizeof(int));
    }
    int a = p[5][8]; // on indexe dans chaque dimension
  • Ceci est une matrice (un tableau de tableau).

Les sanitizers

Problèmes mémoire courants:

  • Dépassement de capacité de tableaux.
  • Utilisation de mémoire non allouée.
  • Fuites mémoire.
  • Double libération.

Outils pour leur détection:

  • Valgrind (outil externe).
  • Sanitizers (ajouts de marqueurs à la compilation).

Ici on utilise les sanitizers (modification de la ligne de compilation):

gcc -o main main.c -g -fsanitize=address -fsanitize=leak

Attention: Il faut également faire l'édition des liens avec les sanitizers.