Ouvrir, vérifier, fermer
Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 8 — Mémoire, fichiers et entrées-sorties
É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érencerNULLau premierfscanf; - appeler
fclose(f)sur le chemin d'échec, alors quefvautNULL; - oublier
fclosesur 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 </em>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.
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.