OVH Cloud OVH Cloud

comprendre fgets

94 réponses
Avatar
bpascal123
Bonjour

Avec p2 qui pointe sur une chaine saisie avec fgets (ex."abcd")
dans for ( p2 ; *p2 ; p2++ )
Pourquoi l'incr=E9mentation se fait sur 6 intervalles en m=E9moire.

0 =3D a
1 =3D b
2 =3D c
3 =3D d
4 =3D \0
5 =3D ???

A quoi correspond la derni=E8re position (numero 5) ?

(En fait, je veux dire que je dois d=E9cr=E9menter 2 fois apres la fin
de la boucle for pour pointer p2 sur 'd' en position 3). Ca veut dire
qu'en plus de '\0', fgets inclue un autre caract=E8re.

Question suppl=E9mentaire (culture g=E9n=E9ral en informatique) :
La chaine enregistr=E9e avec fgets se trouve dans la memoire ram ou
dans un des registres?

Merci

10 réponses

Avatar
espie
In article <4af5f5ae$0$22505$,
candide wrote:
Au passage, je suis quand même allé regarder dans du code réel. Par
exemple dans les GNU core utilities, fichier getdate.c je trouve ceci :

/* ligne 0 */
#if TEST

#include <stdio.h>

int
main (int ac, char **av)
{
char buff[BUFSIZ];
time_t d;

printf ("Enter date, or blank line to exit.nt> ");
fflush (stdout);

buff[BUFSIZ - 1] = 0;
while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
{
d = get_date (buff, 0);
if (d == (time_t) -1)
printf ("Bad format - couldn't convert.n");
else
printf ("%s", ctime (&d));
printf ("t> ");
fflush (stdout);
}
return 0;
}
#endif /* defined TEST */



1/ je ne prendrais pas forcement le code GNU comme exemple de bonnes
pratiques (assez rarement en fait, mais c'est un avis perso).

2/ je ne sais pas si tu le fais expres, mais le #if TEST...#endif indique
assez clairement que ce que tu es en train de lire, c'est un bete test
de regression. Il est assez frequent que ceux-ci soient ecrits avec
nettement moins de soin que du code "reel", puisqu'ils sont normalement
executes dans un environnement parfaitement controle...
Avatar
Benoit Izac
Bonjour,

le 08/11/2009 à 10:52, Alain BARTHE a écrit dans le message
<4af694e5$0$6413$ :

char buff[BUFSIZ];
while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])

d'une part c'est BUFSIZ et non (BUFSIZ - 1) qui devrait être utilisé,
d'autre part tester le retour de fgets est suffisant et je pense que le
« && buff[0] » n'échouera jamais.



Si je ne me trompe pas,

while (fgets (buff, ...) && buff [0])

permet de tester si on a entré une chaine non vide
(dont le premier caractère est différent de ),
à peu près équivalent à écrire : && strlen (buff) > 0



Le problème c'est qu'ici :

char buff[BUFSIZ]; /* buff[0] vaut n'importe quoi */
buff[BUFSIZ - 1] = 0;
while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])

Si on rentre une chaîne vide fgets va renvoyer NULL donc buff[0] ne sera
pas évalué.

De plus, si on imagine que fgets ne renverrai pas NULL (ce qui est faux)
et donc que buff[0] soit évalué, il vaudrait n'importe quoi sur le
premier appel et vaudrait toujours quelque chose sur les appels suivants
car il était déjà valide à l'appel précédent.

--
Benoit Izac
Avatar
-ed-
On 7 nov, 23:33, candide wrote:

Au passage, je suis quand même allé regarder dans du code réel. Par
exemple dans les GNU core utilities, fichier getdate.c je trouve ceci :

/* ligne 0 */
#if TEST

#include <stdio.h>

int
main (int ac, char **av)
{
  char buff[BUFSIZ];
  time_t d;

  printf ("Enter date, or blank line to exit.nt> ");
  fflush (stdout);

  buff[BUFSIZ - 1] = 0;
  while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
    {
      d = get_date (buff, 0);
      if (d == (time_t) -1)
        printf ("Bad format - couldn't convert.n");
      else
        printf ("%s", ctime (&d));
      printf ("t> ");
      fflush (stdout);
    }
  return 0;}

#endif /* defined TEST */

Pourquoi ligne 14 l'auteur place-t-il un 0 en final ?


<...>
Et quand je
vois le peu de temps que les étudiants passent à bosser, je suis en f ait
pas très étonné qu'ils aient un niveau très médiocre en C.



En effet. Ce code est erroné. Il ne fait pas ce qu'il prétend. Ceci
est correct :

#include <stdio.h>
#include <time.h>

static time_t get_date (char const buff[], int n)
{
time_t d = 0;

/* a completer ... */
(void) buff;
(void) n;
return d;
}

static void fclean (char *line, FILE * fp)
{
char *p = strchr (line, 'n');
if (p != NULL)
{
*p = 0;
}
else
{
int c;
while ((c = fgetc (fp)) != 'n' && c != EOF)
{
}
}
}

int main (void)
{
char buff[BUFSIZ];
time_t d;

printf ("Enter date, or blank line to exit.nt> ");
fflush (stdout);

while (fgets (buff, BUFSIZ, stdin) && buff[0] != 'n')
{
fclean (buff, stdin);
d = get_date (buff, 0);
if (d == (time_t) - 1)
printf ("Bad format - couldn't convert.n");
else
printf ("%s", ctime (&d));
printf ("t> ");
fflush (stdout);
}
return 0;
}
Avatar
bpascal123
On 7 nov, 08:53, (Marc Espie) wrote:
In article <4af4a2cc$0$438$,

candide   wrote:



Si tu veux d'autres exemples, je passe aussi pas mal de temps sur les tab leaux,
et je leur dis systematiquement que, dans le doute, il faut verifier l'in dex
avant d'acceder a un element de tableau. Ou sinon, essayer de faire plus
simple dans le code.  Je leur dis aussi qu'ils ne seront pas evalues su r les
astuces tordues qu'ils peuvent mettre dans leur code, bien au contraire. Que
c'est plus difficile de faire du code simple et clair.



Ca fait presque 6 mois que j'apprends le C seul avec livres, tuto,
exercices et usenet... J'ai une formation initiale en comptabilité.

Je passe aussi beaucoup de temps sur les tableaux car pour un premier
langage de programmation je trouve la logique des tableaux assez
proche d'une gestion de données comme on trouve dans les bases de
données... Je sais SQL plus puissant mais c'est que pour les
requêtes...

Y a-t-il un intérêt à approfondir les tableaux en C dans mon cas?
J'essaye actuellement de bien comprendre les tableaux, les pointeurs
et l'arithmétique avant de continuer...

Merci


Dans un autre ordre d'idees, en programmation OO, je vois passer des tas
d'etudiants qui ont tres bien retenu toutes les subtilites de l'heritage
et qui savent quand les arguments seront co/contra-variants, mais qui deg ainent
l'heritage pour absolument tout, et me font des classes valises la ou la
composition est l'outil approprie. Ou bien qui ne connaissent pas des pat tern
aussi utiles que Template Method ou Compiler Firewall...


Avatar
espie
In article ,
wrote:
On 7 nov, 08:53, (Marc Espie) wrote:
In article <4af4a2cc$0$438$,

candide   wrote:



Si tu veux d'autres exemples, je passe aussi pas mal de temps sur les


tableaux,
et je leur dis systematiquement que, dans le doute, il faut verifier l'index
avant d'acceder a un element de tableau. Ou sinon, essayer de faire plus
simple dans le code.  Je leur dis aussi qu'ils ne seront pas evalues sur les
astuces tordues qu'ils peuvent mettre dans leur code, bien au contraire. Que
c'est plus difficile de faire du code simple et clair.



Ca fait presque 6 mois que j'apprends le C seul avec livres, tuto,
exercices et usenet... J'ai une formation initiale en comptabilité.

Je passe aussi beaucoup de temps sur les tableaux car pour un premier
langage de programmation je trouve la logique des tableaux assez
proche d'une gestion de données comme on trouve dans les bases de
données... Je sais SQL plus puissant mais c'est que pour les
requêtes...

Y a-t-il un intérêt à approfondir les tableaux en C dans mon cas?
J'essaye actuellement de bien comprendre les tableaux, les pointeurs
et l'arithmétique avant de continuer...

Merci



Ca depend ce que tu appelles "approfondir" et ton sujet d'etudes precis.

Les tableaux permettent d'explorer plein de concepts algorithmiques a
peu de frais, c'est un investissement fort sympathique de ce point de vue.


Dans la vraie vie, c'est aussi une des structures de donnees les plus
utilisees: un des enormes avantages des tableaux, c'est qu'il n'y a pas
de pointeur (par opposition aux listes, par exemple), donc ca fait des
structures de donnees compactes en memoire ET localisees a un endroit
precis: pour beaucoup de problemes, ca va etre plus efficace que des listes.
(en fait, les listes ne deviennent sympathiques que dans les cas tres
dynamiques, ou on passe son temps a inserer et supprimer des trucs un peu
partout. Pour le reste, c'est souvent le nombre d'acces memoire qui domine le
temps d'execution).

Apres, il y a des points de detail qui ne sont pas forcement tres interessants.
Les tableaux multi-dimensionnels, par exemple, ont une applicabilite somme
toute assez faible. C'est plus interessant de savoir comment faire des
tableaux de taille quelconque (pointeurs, malloc et realloc).

Mieux vaut explorer les tableaux de base en combinaison avec d'autres
elements du langage, e.g., l'utilisation des tableaux de pointeurs (exemple
simple: tableaux de chaines de caracteres), puis les tableaux de pointeurs
de structure.

A ton niveau, c'est sans doute plus important de reflechir aux problemes
de robustesse de ce que tu fais, et d'essayer de voir comment arranger ton
code pour avoir des choses solides et comprehensibles (e.g., faire de
l'idiomatique). En particulier, le fait que les tableaux ne sont pas
checkes en taille, ce qui impose rapidement de devoir passer la taille
reelle du tableau en parametre supplementaire... puis les tableaux dynamiques
qui conduisent rapidement au pattern standard size/capacity...
Avatar
Benoit Izac
Dans le message , le 08/11/2009 à 11:56, j'ai
écrit :

while (fgets (buff, ...) && buff [0])

permet de tester si on a entré une chaine non vide
(dont le premier caractère est différent de ),
à peu près équivalent à écrire : && strlen (buff) > 0



Le problème c'est qu'ici :

char buff[BUFSIZ]; /* buff[0] vaut n'importe quoi */
buff[BUFSIZ - 1] = 0;
while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])

Si on rentre une chaîne vide fgets va renvoyer NULL donc buff[0] ne sera
pas évalué.

De plus, si on imagine que fgets ne renverrai pas NULL (ce qui est faux)
et donc que buff[0] soit évalué, il vaudrait n'importe quoi sur le
premier appel et vaudrait toujours quelque chose sur les appels suivants
car il était déjà valide à l'appel précédent.



J'ai (encore) dit une bêtise. fgets() ne renvoie pas NULL si on a ''
en entrée.

J'avais fais mon test avec
% printf '' | ./mon_prog
or ça renvoie EOF
% printf '%c' '' | ./mon_prog
envoie bien un ''

Par curiosité, j'aimerai bien savoir comment on fait pour envoyer un
'' lors d'une saisie (sous UNIX).

Dans tous les cas, avoir un '' en entrée sur un fichier texte est peu
vraisemblable.

--
Benoit Izac
Avatar
Benoit Izac
Bonjour,

le 08/11/2009 à 15:51, Éric Lévénez a écrit dans le message
<4af6db0f$0$16708$ :

Par curiosité, j'aimerai bien savoir comment on fait pour envoyer un
'' lors d'une saisie (sous UNIX).



Ctrl-@



Super ! Je savais que ctrl-a => 1, ctrl-b => 2, ..., ctrl-z => 26 (c'est
facile de s'en souvenir), mais impossible de trouver le 0. Je viens
de m'apercevoir que tout ceci est documenté dans ascii(7).

Dans tous les cas, avoir un '' en entrée sur un fichier texte est peu
vraisemblable.



Pas plus invraisemblable qu'un autre caractère. Les outils GNU
utilisent très souvent le NUL comme séparateur de nom de fichier. Voir
"find" par exemple.



Mauvais exemple, find n'enverra pas un '' en premier caractère (''
ne peut pas apparaître dans un nom de fichier) et je doute que fgets()
permette de traiter la sortie d'un find -print0 car il est impossible de
savoir si le '' fait parti de la chaîne ou a été rajouté par fgets().

De plus, dans son exemple, ça commençait tout de même par un
printf ("Enter date, or blank line to exit.nt> ");
ce qui sous-entend que la saisie va ce faire au clavier.

--
Benoit Izac
Avatar
Marc Boyer
Le 07-11-2009, -ed- a écrit :
On 6 nov, 23:25, candide wrote:
Marc Boyer a écrit :
Ici et dans tes messages précédents, tu as parfaitement traduit les
réactions des étudiants (et encore d'étudiants qui réagissent !)

>   Non, les codes de retour, c'était la partie "maintenant,
> on fait attention",
> et ça va aussi avec les retours de malloc, etc.

Absolument. Question de progressivité qui s'impose pour moi comme une
évidence.



Une fois de plus, le C n'est pas un langage pour débutants en
informatique... Trop de subtilités à gérer d'un coup...



Voui. En même temps, c'est difficile de s'en passer dans certaines
formations, qui vont devoir aller écrire un peu de C de temps en temps,
sans que ce soit leur boulot.

Certains
verront alors le C comme un langage compliqué, oui, c'est possible,
car la complexité est 'cachée' dans les autres langages. Ici, on est
proche du fonctionnement réel, donc les choses sont un peu moins
'bisounours'... C'est le prix à payer pour avoir du code efficace.



Non, le C est aussi compliqué parce que les gens qui l'ont fait
on commis des erreurs, et que la compatibilit ascendante était une
priorité.
Le fait que char soit parfois signé, parfois non n'a rien à voir
avec les perfs.
La conversion des signés en non-signés lors d'opérations mixtes
signés/non signé n'a rien à voir avec les perfs.
Le fait de garder gets n'a rien à voir avec les perfs.
La sémantique de getc n'a rien à voir avec les perfs.
La conversion implicite des tableaux en pointeur n'a pas grand
chose à voir avec les perfs.
La syntaxe des pointeurs de fonction n'a rien à voir avec les perfs.
L'absence de type booléen facile à utiliser n'a pas grand chose
à voir avec les perfs.
La nécessité de faire un #ifndef/#define n'a rien à voir avec les
perfs.

Ceci-dit, les E/S en Java, c'est pas d'une simplicité biblique non
plus... Quand au Pasca standard, ça n'existe carrément pas !
Heureusement, il y a Turbo Pascal (ou freePascal).



Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
Avatar
Marc Boyer
Le 06-11-2009, Marc Espie a écrit :
Pour scanf, c'est quand meme tres simple: ca renvoie le nombre de valeurs
converties avec succes, et autre chose en cas de pepin.

C'est pas dur de faire

r = scanf("%d", &d);
if (r != 1) {
fprintf(stderr, "Chef, on a un problemen");
abort();
}

Pour getc: expliquer getc avant de parler d'EOF, ca me parait aberrant.



Donc tu parles des flux, ce truc étrange où ce que tu lis
disparaît, et puis EOF, c'est un entier qu'on ne peut pas coder
sur un char, sauf que c'est -1 en pratique (il va vite y avoir
un étudiant qui va faire un printf("%d",EOF)), mais qu'en fait,
c'est en temps qu'unsigned char qu'il est pas représentable...

Dans le pire des cas, je suis pret a leur donner un idiome qui marche,
a utiliser tel quel, quitte a expliquer certains details apres...



J'ai hésité un temps à fournir une mini-bibliothèque:
int NEWBY_readInt();
char NEWBY_readChar();
mais j'ai laissé tombé.
L'idée de candide de faire aucune E/S pendant le premier quart
du cours m'intéressait aussi.

En fait, mon "idiome qui marche", ce fut "on va faire comme
si le monde était gentil pour le moment".

Puis, plus tard, la vraie vie "ça y est, vous venez de faire
exploser la fusée..."

Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
Avatar
espie
In article <hd8pmk$eau$,
Marc Boyer wrote:
Le 06-11-2009, Marc Espie a écrit :
Pour scanf, c'est quand meme tres simple: ca renvoie le nombre de valeurs
converties avec succes, et autre chose en cas de pepin.

C'est pas dur de faire

r = scanf("%d", &d);
if (r != 1) {
fprintf(stderr, "Chef, on a un problemen");
abort();
}

Pour getc: expliquer getc avant de parler d'EOF, ca me parait aberrant.



Donc tu parles des flux, ce truc étrange où ce que tu lis
disparaît, et puis EOF, c'est un entier qu'on ne peut pas coder
sur un char, sauf que c'est -1 en pratique (il va vite y avoir
un étudiant qui va faire un printf("%d",EOF)), mais qu'en fait,
c'est en temps qu'unsigned char qu'il est pas représentable...

Dans le pire des cas, je suis pret a leur donner un idiome qui marche,
a utiliser tel quel, quitte a expliquer certains details apres...



J'ai hésité un temps à fournir une mini-bibliothèque:
int NEWBY_readInt();



Ben ca c'est exactement mon scanf() avec controle d'erreur rudimentaire.
On leur dit que voila, faut taper un entier suivi par "entree", et que s'ils
font autre chose, va y avoir des bricoles...

(rappel: de toutes facons, on ne peut pas flusher un flux d'entree de
facon portable, donc s'ils veulent des trucs plus funky, va falloir attendre...)