Déjà une trentaine de pages vues depuis hier, c'est un bon début, merci aux lecteurs !
Ce billet ne constitue pas vraiment l'apprentissage d'une notion en particulier comme cela avait été fait hier, mais plutôt une petite analyse du fonctionnement de la bibliothèque standard de C.
Comme point de départ aujourd'hui, ce sera le titre d'un essai d'Éric Raymond, le célèbre hacker américain. Fervent défenseur du terme open source, il s'oppose au mode de développement de GNU et de Linux, le qualifiant de bazar.
La bibliothèque standard de C est souvent assimilée à ce mode de développement, et l'exemple léger que nous allons voir aujourd'hui peut potentiellement constituer une preuve du manque de rigueur du bazar.
Vous avez sûrement déjà rencontré des fonctions qui manipulent des « éléments » d'une taille spécifiée plutôt que des bytes directement : qsort, bsearch, fread, fwrite ou encore calloc par exemple. Chacune de ses fonctions possède deux arguments pour spécifier la taille d'un élément et le nombre d'éléments.
Jusque là, pas de problème. Mais quand on regarde de plus près, on se rend compte qu'il y a des problèmes d'uniformité. Voyez par exemple ces deux prototypes :
void *calloc(size_t nmemb, size_t size);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
On voit très distinctement que l'ordre des arguments est modifié. Dans un cas, on a « nmemb » suivi de « size » et dans l'autre, on a « size » suivi de « nmemb ».
Bon, et concrètement, qu'est-ce que ça change ? De toute manière, le nombre total de bytes manipulés sera égal au produit des arguments, et, quel que soit l'ordre dans lesquels on les spécifie, ce sera la même chose, non ?
En fait, là où ça coince, c'est dans la valeur retournée par la fonction : c'est le nombre d'éléments (nmemb) manipulés, et pas le nombre de bytes. Si on lit 5 éléments de 3 bytes ou 3 éléments de 5 bytes, le résultat sera sans doute identique, mais la valeur de retour de la fonction sera de 5 éléments dans un cas, et de 3 éléments dans l'autre.
En supposant que la valeur retournée permet de représenter combien d'éléments votre programme doit maintenant traiter, vous allez garder la taille et le nombre d'éléments directement, de peur que vous essayiez de lire 5 éléments et d'en traiter 3, ou bien de lire 3 éléménts et d'en traiter 5 !
Un problème avec le report d'éléments lus et écrits est quand vous avez une erreur d'entrées/sorties ou une fin de fichier au milieu d'un élément. Il est alors de difficile de déterminer exactement ce que vous avez lu. Par exemple, si vous essayiez de lire 5 éléments de 3 bytes et fread vous retourne 2, vous ne pouvez pas savoir si vous avez lu exactement 2 éléments, ou bien 2 et 1 byte puis EOF, etc. Pour contourner cela, on peut utiliser la taille de l'élément.
size_t bytesRead = fread(p, 1UL, 5 * sizeof *p, fp);
// De la même manière, on peut récupérer le nombre d'éléments à traiter
size_t nmemb = bytesRead / sizeof *p;
// On peut déterminer si on a été stoppé au milieu d'un élément.
if (bytesRead % sizeof *p) {
/* on a été stoppé au milieu d'un élément */
}J'ai vu pour la première fois cette idée exposée sur CCL, ça m'a paru anecdotique mais intéressant pour avoir un peu de culture geekeste !
Aucun commentaire:
Enregistrer un commentaire