Comment fonctionne le fread?

La déclaration de fread est la suivante:

 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 

La question est la suivante: y a-t-il une différence dans les performances de lecture de deux appels de ce type:

 char a[1000]; 
  1. fread(a, 1, 1000, stdin);
  2. fread(a, 1000, 1, stdin);

Va-t-il lire 1000 octets à la fois à chaque fois?

Il peut y avoir ou non une différence de performance. Il y a une différence de sémantique.

 fread(a, 1, 1000, stdin); 

tente de lire 1000 éléments de données, chacun ayant une longueur d’un octet.

 fread(a, 1000, 1, stdin); 

tente de lire 1 élément de données de 1000 octets de long.

Ils sont différents car fread() renvoie le nombre d’éléments de données qu’il était capable de lire, pas le nombre d’octets. S’il atteint la fin du fichier (ou une condition d’erreur) avant de lire les 1000 octets complets, la première version doit indiquer exactement le nombre d’octets qu’il a lu. le second échoue et renvoie 0.

En pratique, cela va probablement appeler une fonction de bas niveau qui tente de lire 1000 octets et indique le nombre d’octets réellement lus. Pour les lectures plus importantes, il peut effectuer plusieurs appels de niveau inférieur. Le calcul de la valeur à retourner par fread() est différent, mais le coût du calcul est sortingvial.

Il peut y avoir une différence si l’implémentation peut dire, avant d’essayer de lire les données, qu’il n’y a pas assez de données à lire. Par exemple, si vous lisez à partir d’un fichier de 900 octets, la première version lira tous les 900 octets et renverra 900, tandis que la seconde risque de ne rien lire. Dans les deux cas, l’indicateur de position du fichier est avancé du nombre de caractères lus avec succès, soit 900.

Mais en général, vous devriez probablement choisir comment l’appeler en fonction des informations dont vous avez besoin. Lire un seul élément de données si une lecture partielle ne vaut pas mieux que de ne rien lire du tout. Lire des blocs plus petits si des lectures partielles sont utiles.

Selon la spécification , les deux peuvent être traités différemment par la mise en œuvre.

Si votre fichier est inférieur à 1000 octets, fread(a, 1, 1000, stdin) (lire 1000 éléments de 1 octet chacun) copiera toujours tous les octets jusqu’à EOF. D’un autre côté, le résultat de fread(a, 1000, 1, stdin) (lire 1 1000 octets) stocké dans a n’est pas spécifié, car il n’y a pas assez de données pour terminer la lecture du premier (et uniquement) 1000 élément d’octet.

Bien sûr, certaines implémentations peuvent toujours copier l’élément “partiel” en autant d’octets que nécessaire.

Ce serait la mise en œuvre détaillée. Dans la glibc, les deux sont identiques en termes de performances, car elles sont implémentées essentiellement comme suit: (réf. http://sourceware.org/git/?p=glibc.git;a=blob;f=libio/iofread.c ):

 size_t fread (void* buf, size_t size, size_t count, FILE* f) { size_t bytes_requested = size * count; size_t bytes_read = read(f->fd, buf, bytes_requested); return bytes_read / size; } 

Notez que le C et POSIX standard ne garantit pas qu’un object complet de size doit être lu à chaque fois. Si un object complet ne peut pas être lu (par exemple, stdin ne possède que 999 octets mais que vous avez demandé une size == 1000 ), le fichier sera laissé dans un état interdépendant (C99 § 7.19.8.1 / 2).

Edit: Voir les autres réponses à propos de POSIX.

fread appelle getc interne. Dans Minix nombre de fois que getc est appelé est simplement la size*nmemb . Le nombre de fois que getc sera appelé dépend du produit de ces deux. Donc les deux fread(a, 1, 1000, stdin) et fread(a, 1000, 1, stdin) exécuteront getc 1000=(1000*1) Times. Voici l’implémentation simple du fread de Minix

 size_t fread(void *ptr, size_t size, size_t nmemb, register FILE *stream){ register char *cp = ptr; register int c; size_t ndone = 0; register size_t s; if (size) while ( ndone < nmemb ) { s = size; do { if ((c = getc(stream)) != EOF) *cp++ = c; else return ndone; } while (--s); ndone++; } return ndone; } 

Il n’y a peut-être pas de différence de performance, mais ces appels ne sont pas les mêmes.

  • fread renvoie le nombre d’éléments lus, donc ces appels renverront des valeurs différentes.
  • Si un élément ne peut pas être complètement lu, sa valeur est indéterminée:

Si une erreur se produit, la valeur résultante de l’indicateur de position du fichier pour le stream est indéterminée. Si un élément partiel est lu, sa valeur est indéterminée. (ISO / IEC 9899: TC2 7.19.8.1)

Il n’y a pas beaucoup de différence dans l’ implémentation de la glibc , qui multiplie simplement la taille de l’élément par le nombre d’éléments pour déterminer le nombre d’octets à lire et divise le montant lu par la taille du membre à la fin. Mais la version spécifiant une taille d’élément de 1 vous indiquera toujours le nombre correct d’octets lus. Cependant, si vous ne vous souciez que de lire des éléments d’une certaine taille, l’utilisation de l’autre forme vous évite de faire une division.

Un autre formulaire de phrase http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html est remarquable

La fonction fread () doit lire dans le tableau pointé par ptr jusqu’aux éléments nitems dont la taille est spécifiée par taille en octets, à partir du stream pointé par stream. Pour chaque object, des appels de taille doivent être faits à la fonction fgetc () et les résultats stockés , dans l’ordre lu, dans un tableau de caractères non signés recouvrant exactement l’object.

Dans les deux cas, les données seront accessibles par fgetc () …!

Je voulais clarifier les réponses ici. fread effectue des entrées-sorties tamponnées. Les tailles de blocs de lecture réelles utilisées par les utilisateurs sont déterminées par l’implémentation C utilisée.

Toutes les bibliothèques C modernes auront les mêmes performances avec les deux appels:

 fread(a, 1, 1000, file); fread(a, 1000, 1, file); 

Même quelque chose comme:

 for (int i=0; i<1000; i++) a[i] = fgetc(file) 

Devrait aboutir aux mêmes modèles d'access au disque, bien que fgetc soit plus lent en raison de plus d'appels dans les bibliothèques c standard et dans certains cas, la nécessité d'un disque pour effectuer des recherches supplémentaires qui auraient autrement été optimisées.

Revenir à la différence entre les deux formes de fread. Le premier renvoie le nombre réel d'octets lus. Ce dernier retourne 0 si la taille du fichier est inférieure à 1000, sinon il renvoie 1. Dans les deux cas, le tampon serait rempli avec les mêmes données, à savoir le contenu du fichier jusqu'à 1000 octets.

En général, vous souhaitez probablement conserver le paramètre 2 (taille) à 1 pour obtenir le nombre d'octets lus.