Discussion:Programmation C/Entrées/sorties
Dernier commentaire : il y a 16 ans par Thierry46 dans le sujet Problèmes dans exemple
Problèmes dans exemple
modifierBonjour
Dans le chapitre entrées non formatées, je lis :
Un exemple de lecture de ligne arbitrairement longue est vu plus bas...
L'exemple incorrect
modifierL'exemple incorrect
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/**
* lire_ligne tente de lire une ligne entiere depuis le flux.
* Le '\n' final est enlevé.
*
* Si la ligne est vide, la fonction renvoie un pointeur vers
* une chaîne contenant uniquement '\0' et place errno à 0.
*
* Si une erreur intervient au début de la lecture,
* la fonction renvoie NULL et place errno à 1.
*
* Si une erreur intervient après qu'au moins un caractère a été lu,
* la fonction renvoie ce qui a été lu, et place errno à 1.
*
* En cas de succès, errno est placé à 0.
*/
char *lire_ligne(FILE *flux)
{
int taille = 256;
char *chaine = malloc(taille);
errno = (chaine == NULL);
if (chaine != NULL)
{
int i = 0;
int c;
while ((c = fgetc(stdin)) != EOF)
{
if (i == taille)
{
/* Le tableau est plein, et on a encore des choses a lire... */
taille += 256;
char *nouvelle = realloc(chaine, taille);
if (nouvelle == NULL)
{
errno = 1;
break;
}
chaine = nouvelle;
}
if (c != '\n')
{
chaine[i] = c;
i++;
}
else
{
/* fin de la ligne: on s'arrete sans mettre le '\n' */
break;
}
}
chaine[i] = '\0';
}
return chaine;
}
Mes remarques
modifier- A quoi sert
#include <string.h>
? - A quoi sert l'argument
FILE *flux
? La fonction ne lit que dans stdinfgetc(stdin)
. - Utiliser errno à droite dans une affectation pour retourner des valeurs perso me parait risqué :
errno = (chaine == NULL);
. Le système initialise errno à ENOMEM pour un problème surmalloc
ourealloc
, pourquoi ne pas s'en servir ? Une initialisation de errno à 1, correspond à une erreur système EPERM : Operation not permitted. L'emploi de perror dans l'appelant affichera un mauvais diagnostique d'erreur. - Pourquoi utiliser
fgetc
au lieu defgets
, quitte à appeler plusieurs fois fgets si la ligne est trop longue pour le buffer chaine ? - L'utilisation de
break
dans une boucle est déconseillée. - La mémoire est allouée. Pour éviter les fuites mémoires, l'appelant devra la libérer. Cela devrait être stipulé dans les commentaires d'entête.
- Comment sait-on si avec avec
fgetc
on a rencontré une fin de fichier ou ou erreur ? - Si dans la boucle (i == taille-1) et (c != '\n'), on incrémente i qui vaut taille, si à l'itération suivante fgetc retourne EOF, on sort de la boucle. L'instruction
chaine[i] = '\0';
adresse chaine[taille]. chaine a des indices valides de 0 à taille-1. Cette instruction ècrit en dehors du tableau. Il y a du SIGSEGV (violation d'espace d'adressage) dans l'air. Il faut toujours imaginer le pire. - La constante littérale 256 devrait être remplacée par une constante symbolique.
malloc
etrealloc
attendent un argument de type size_t, on leur passe taille de type int.- La déclaration
char *nouvelle
devrait être en premier dans son bloc. - Les expressions idiomatiques C
errno = (chaine == NULL);
ettaille += 256;
peuvent être difficiles à comprendre par un autre programmeur chargé de la maintenance et non habitué à cette syntaxe spécifique du C. - Pourquoi ne pas déclarer le paramètre de cette fonction :
FILE * restrict flux
? - Pourquoi ne pas fournir un programme
main
appelant pour tester cette fonction ?
Ma solution
modifierExercices en langage C/Fonctions#Lire une ligne longue avec fgets