Adloun

Probleme – Compilation séparée : une pile en module

Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 1 — Le langage C

Énoncé

On veut offrir une pile d'entiers dont l'utilisateur ignore la réalisation.

Corrigé

1. L'interface.


/* pile.h */
#ifndef PILE_H
#define PILE_H
#include <stdbool.h>

typedef struct pile_s pile;      /* type OPAQUE : la structure reste cachée */

pile* pile_creer(void);          /* pile vide, ou NULL si échec */
bool  pile_est_vide(const pile* p);
bool  pile_empiler(pile* p, int x);   /* false si l'allocation échoue */
int   pile_depiler(pile* p);          /* précondition : p non vide */
void  pile_detruire(pile* p);

#endif

Les trois lignes #ifndef / #define / #endif rendent l'inclusion idempotente : inclure deux fois le fichier ne déclare qu'une fois.

2. La réalisation.


/* pile.c */
#include "pile.h"
#include <stdlib.h>
#include <assert.h>

struct pile_s { int* t; int n; int capacite; };   /* invisible à l'extérieur */

pile* pile_creer(void) {
    pile* p = malloc(sizeof(pile));
    if (p == NULL) { return NULL; }
    p->t = malloc(4 * sizeof(int));
    if (p->t == NULL) { free(p); return NULL; }
    p->n = 0; p->capacite = 4;
    return p;
}

bool pile_est_vide(const pile* p) { assert(p != NULL); return p->n == 0; }

bool pile_empiler(pile* p, int x) {
    assert(p != NULL);
    if (p->n == p->capacite) {
        int* d = malloc((size_t) 2 * p->capacite * sizeof(int));
        if (d == NULL) { return false; }
        for (int i = 0; i < p->n; i = i + 1) { d[i] = p->t[i]; }
        free(p->t); p->t = d; p->capacite = 2 * p->capacite;
    }
    p->t[p->n] = x; p->n = p->n + 1;
    return true;
}

int pile_depiler(pile* p) {
    assert(p != NULL && p->n > 0);
    p->n = p->n - 1;
    return p->t[p->n];
}

void pile_detruire(pile* p) { if (p != NULL) { free(p->t); free(p); } }

3. Ce que l'opacité interdit et permet.

Elle interdit : déclarer une pile sur la pile d'appel (sa taille est inconnue du compilateur, d'où l'allocation obligatoire), et lire ou écrire un champ depuis l'extérieur. Un utilisateur ne peut pas casser l'invariant : les seules fonctions qui touchent ces champs sont les cinq du module.

Elle permet : remplacer la réalisation par une chaîne de maillons, ou par un tableau à capacité fixe, sans recompiler ni relire une seule ligne du code utilisateur. C'est la modularité que le chapitre chap:abstraction détaillera, et elle se paye ici d'une indirection.

4. La compilation séparée.


gcc -Wall -Wextra -std=c99 -c pile.c        -o pile.o
gcc -Wall -Wextra -std=c99 -c programme.c   -o programme.o
gcc pile.o programme.o -o programme

Chaque .c est compilé séparément — d'où le nom — puis les fichiers objets sont liés. Modifier pile.c ne demande de recompiler que lui.

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.