Adloun

informatique (MP2I/MPI) · Chapitre 8 · Exercice de TD

Ouvrir, vérifier, fermer

Mémoire, fichiers et entrées-sorties · niveau 2

Énoncé

Écrire une fonction qui rend la somme des entiers contenus dans un fichier, en distinguant l'échec d'ouverture du fichier vide. Donner sa spécification.

Corrigé

Le premier travail n'est pas d'écrire la boucle, c'est de décider ce que la fonction rend dans chaque cas. Trois situations sont possibles — fichier absent, fichier vide, fichier lisible — et la valeur ne peut pas les distinguer. On rend donc le succès à part.


/* Range dans *somme la somme des entiers ecrits dans le fichier chemin.
   Renvoie le nombre d'entiers lus, ou -1 si le fichier n'a pas pu etre ouvert.
   Precondition  : chemin et somme non nuls.
   Postcondition : en cas de succes, *somme est la somme des entiers lus ;
                   un fichier vide donne 0 entiers lus et *somme = 0. */
int somme_fichier(const char* chemin, long* somme) {
    assert(chemin != NULL && somme != NULL);
    FILE* f = fopen(chemin, "r");
    if (f == NULL) { return -1; }        /* PAS de deref, PAS de fclose */
    *somme = 0;
    int combien = 0, x = 0;
    /* INVARIANT : *somme est la somme des `combien` premiers entiers du
       fichier, et la position courante est juste apres le dernier lu. */
    while (fscanf(f, "%d", &x) == 1) {
        *somme = *somme + x;
        combien = combien + 1;
    }
    fclose(f);
    return combien;
}

Terminaison. Le variant est le nombre d'octets restant à lire : chaque tour réussi en consomme au moins un, et fscanf rend autre chose que dès qu'il n'y a plus d'entier à lire. Complexité : où est la taille du fichier en octets, et en mémoire — on ne stocke rien.

Les quatre fautes que cette écriture évite, et qui sont les quatre fautes classiques du chapitre :

  • ne pas tester fopen, et déréférencer NULL au premier fscanf ;
  • appeler fclose(f) sur le chemin d'échec, alors que f vaut NULL ;
  • oublier fclose sur le chemin de succès — une fuite de descripteur, invisible tant qu'on n'ouvre pas mille fichiers ;
  • confondre « somme nulle » et « échec » en rendant un seul long.

Le type de *somme. On l'a pris long et non int : une somme déborde bien plus vite qu'un terme, et un débordement d'entier signé est silencieux (chapitre chap:langage-c). Une version défensive complète testerait aussi ce débordement avant chaque addition.

Exercices semblables

Le cours du chapitre · Tous les exercices du chapitre