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 <4af4a363$0$438$,
candide wrote:

Les entrees sont compliquees. C'est le cas dans tous les langages.
Il faudra bien tot ou tard l'expliquer a tes debutants.




Bof, ça dépend à qui tu t'adresses. Il y a
énormément de gens qui ne font du C qu'à
l'école ou à la fac.



Je ne vois pas l'interet de mettre du C dans des formations si il n'y a pas
une grande chance que les gens s'en reservent plus tard. Il y a des tonnes
de langages plus appropries pour ces gens-la (ou on echange les subtilites
bas-niveau de la libc pour les subtilites plus haut niveau liees au fait que
tu as un etre humain devant le clavier...)

Sauf absurdite manifeste du cursus, tu as des gens qui vont passer par celui-ci
et qui vont ensuite pondre du C dans l'industrie. Enseigner gets() a ces
gens-la, c'est ni plus ni moins qu'une faute professionnelle. Et je pese
mes mots.
Avatar
espie
In article <4af4b559$0$24022$,
candide wrote:
Marc Espie a écrit :

Mais sortir gets de sa poubelle, surtout avec tous les arguments
fallacieux que tu avances:
1/ c'est grotesque
2/ c'est une faute qui peut avoir des consequences graves.




Le Rationale est pourtant bien modéré concernant cette fonction :

The Committee decided that gets was useful and convenient in those
special circumstances when the programmer does have adequate control
over the input, and as longstanding existing practice, it needed a
standard specification.



Le comite a tort, ca lui arrive. Le monde est de plus en plus hostile,
avec des possibilites de piratage un peu partout, et de l'informatique
qui est utilisee de maniere routiniere pour des missions critiques, ou
l'echec peut occasionner des pertes de vies humaines. Il est plus que temps
d'essayer de monter un peu le niveau et d'essayer de limiter la casse.

Il ne faut jamais oublier que le comite represente la communaute du C dans
son ensemble, et que celle-ci contient aussi des debiles qui ne se sont
pas encore reveilles.
Avatar
-ed-
On 6 nov, 23:25, candide wrote:
Marc Boyer a écrit :

>   Et c'est quoi le code d'erreur de scanf ? Et pourquoi getc ne retou rne pas
> un char ? Et ça veut dire quoi que la valeur de retour est "unsigned char
> cast to an int" ?

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... Par contre,
pour les habitués d'un autre langage de programmation, passer un peu
de temps pour expliquer ces subtilités n'est pas du luxe. 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.

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).
Avatar
candide
-ed- a écrit :

Une fois de plus, le C n'est pas un langage pour débutants en
informatique...



Phrase que tu nous ressors régulièrement et qui n'a en fait aucun sens
tant les expériences sont diverses et tant cela dépend du contenu que tu
vas mettre dans "le C". Plus qu'une question de difficulté intrinsèque,
c'est une question (à capacités égales) de :

1°) qualité de l'exposition (en particulier la progressivité, l'absence
de surcharge, la contextualisation, le choix des exemples)
2°) motivation (en particulier la finalité de formation) de celui qui
apprend.



Trop de subtilités à gérer d'un coup...



Effectivement et c'est pour cela qu'il faut "paralléliser" les
difficultés à exposer ce que tu as du mal à comprendre.


Et c'est tellement vrai qu'en lisant ton site je me rends compte que tu
n'as pas complètement compris comment fonctionne fgets(). En effet, je
lis :




-------------- http://www.bien-programmer.fr/notes.php --------------
Gestion des fins de ligne

On constate que lorsque fgets() lit une ligne entière, un 'n' se
retrouve à la fin de la chaine saisie. La présence de 'n' est gênante
ou non selon l'application.

Ceci dit, dans tous les cas, il est conseillé d'en détecter la présence.
En effet, sa présence indique que la ligne a été lue entièrement, alors
que son absence indique que la ligne a été tronquée, et que d'autres
caractères (au minimum un 'n') attendent pour être lus.
---------------------------------------------------------------------




Je crois que c'est incorrect car si je termine le flux par eof, alors
fgets() ne renverra pas NULL et il n'y aura pas de 'n' (le buffer est
vide) et pourtant la ligne a été lue entièrement.
Avatar
espie
In article <4af5873e$0$279$,
candide wrote:
Je crois que c'est incorrect car si je termine le flux par eof, alors
fgets() ne renverra pas NULL et il n'y aura pas de 'n' (le buffer est
vide) et pourtant la ligne a été lue entièrement.



... c'est effectivement un classique des tests, s'assurer que ca marche
encore meme s'il n'y a pas de n en fin de fichier.

Cote description, la page de man d'OpenBSD est on ne peut plus claire, et
rend la fonction parfaitement utilisable.

[Quitte a faire de la pub, c'est tres souvent le cas. C'est en grande
partie lie au fait que le projet utilise ces fonctions dans des conditions
reelles, et qu'il y a des gens que ca interesse de rendre ces pages les
plus claires possibles.]

DESCRIPTION

The fgets() function reads at most size-1 characters from the given
stream and stores them in the string str. Reading stops when a newline
character is found, at end-of-file, or on error. The newline, if any, is
retained. The string will be NUL-terminated if fgets() succeeds; other-
wise the contents of str are undefined.

The gets() function is equivalent to fgets() with an infinite size and a
stream of stdin, except that the newline character (if any) is not stored
in the string. It is the caller's responsibility to ensure that the in-
put line, if any, is sufficiently short to fit in the string.

[nota bene: il n'est pas dit comment on peut s'assurer que la ligne en entree
va marcher pour gets(). Il n'y a pratiquement peu de cas ou l'appelant peut
assurer cette responsabilite -> gets() ne sert a rien].

RETURN VALUES
Upon successful completion, fgets() and gets() return a pointer to the
string. If end-of-file or an error occurs before any characters are
read, they return NULL. The fgets() and gets() functions do not distin-
guish between end-of-file and error, and callers must use feof(3) and
ferror(3) to determine which occurred. Whether fgets() can possibly fail
with a size argument of 1 is implementation-dependent. On OpenBSD,
fgets() will never return NULL when size is 1.


Les points cruciaux sont:
- il faut toujours verifier si fgets a reussi.
- le resultat est NUL-terminated des lors que la fonction a reussi.
- en cas d'erreur, il faut utiliser feof et ferror pour savoir ce qui
s'est passe.
- il ne faut pas dependre de particularites de l'implementation si size==1.

Le reste (comment voir si on a lu une ligne complete, comment lire une
ligne complete dans un tampon de taille variable, comment detecter et
signaler la fin de fichier) se deduit trivialement(*) de la description.

*: au sens mathematique usuel. La description est suffisamment precise pour
qu'on puisse bosser avec, surtout si on la complete avec celle de feof et
ferror. Et on peut considerer que c'est a la portee de n'importe qui dote
d'un cerveau en etat de fonctionner, chose un peu rare de nos jours.


Une autre facon de voir, c'est de dire que ca n'a pas de sens d'enseigner
le C a des gens:
- qui n'ont pas les prerequis necessaires;
- qui sont trop cons pour comprendre (desole, c'est pas du tout
politically-correct).

Du coup, c'est legitime de larguer le bagage en trop, et d'enseigner quelque
chose de correct pour les gens qui vont s'en servir en vrai.

A mon sens, le C correct est a la portee de qui s'en donne la peine. C'est
pas simple, ca demande du boulot et de l'opiniatrete. Mais il n'y a pas de
place pour les feignants qui ne feront pas les efforts necessaires, et ca ne
fait aucun sens de faire de la demagogie et d'essayer de faire croire que c'est
un langage simple et accessible au debutant sans faire d'efforts.
Avatar
Manuel Pégourié-Gonnard
Marc Espie scripsit :

Cote description, la page de man d'OpenBSD est on ne peut plus claire, et
rend la fonction parfaitement utilisable.

[Quitte a faire de la pub, c'est tres souvent le cas. C'est en grande
partie lie au fait que le projet utilise ces fonctions dans des conditions
reelles, et qu'il y a des gens que ca interesse de rendre ces pages les
plus claires possibles.]



Merci pour la pub, je note. En plus on peut les consulter en ligne,
c'est pratique.

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
Benoit Izac
Bonjour,

le 07/11/2009 à 18:01, Manuel Pégourié-Gonnard a écrit dans le message
<hd4961$vtg$ :

Marc Espie scripsit :

Cote description, la page de man d'OpenBSD est on ne peut plus claire, et
rend la fonction parfaitement utilisable.

[Quitte a faire de la pub, c'est tres souvent le cas. C'est en grande
partie lie au fait que le projet utilise ces fonctions dans des conditions
reelles, et qu'il y a des gens que ca interesse de rendre ces pages les
plus claires possibles.]



Merci pour la pub, je note. En plus on peut les consulter en ligne,
c'est pratique.



Si tu veux de la doc pas trop mal faite et disponible à tout moment via
la commande man, je te conseille les pages de manuel issues de SUSv3.
Elles sont disponibles sur toutes bonnes distributions sinon c'est là :
<http://www.kernel.org/pub/linux/docs/man-pages/man-pages-posix/>.

Je me permets de poster un exemple pour fgets(3) :
------------------------------------------------------------------------------
FGETS(3P) POSIX Programmer's Manual FGETS(3P)

PROLOG
This manual page is part of the POSIX Programmer's Manual. The Linux
implementation of this interface may differ (consult the corresponding
Linux manual page for details of Linux behavior), or the interface may
not be implemented on Linux.

NAME
fgets - get a string from a stream

SYNOPSIS
#include <stdio.h>

char *fgets(char *restrict s, int n, FILE *restrict stream);

DESCRIPTION
The fgets() function shall read bytes from stream into the array
pointed to by s, until n-1 bytes are read, or a <newline> is read and
transferred to s, or an end-of-file condition is encountered. The
string is then terminated with a null byte.

The fgets() function may mark the st_atime field of the file associated
with stream for update. The st_atime field shall be marked for update
by the first successful execution of fgetc(), fgets(), fgetwc(),
fgetws(), fread(), fscanf(), getc(), getchar(), gets(), or scanf()
using stream that returns data not supplied by a prior call to ungetc()
or ungetwc().

RETURN VALUE
Upon successful completion, fgets() shall return s. If the stream is at
end-of-file, the end-of-file indicator for the stream shall be set and
fgets() shall return a null pointer. If a read error occurs, the error
indicator for the stream shall be set, fgets() shall return a null
pointer, and shall set errno to indicate the error.

ERRORS
Refer to fgetc().

The following sections are informative.

EXAMPLES
Reading Input
The following example uses fgets() to read each line of input.
{LINE_MAX}, which defines the maximum size of the input line, is
defined in the <limits.h> header.

#include <stdio.h>
...
char line[LINE_MAX];
...
while (fgets(line, LINE_MAX, fp) != NULL) {
...
}
...

APPLICATION USAGE
None.

RATIONALE
None.

FUTURE DIRECTIONS
None.

SEE ALSO
fopen(), fread(), gets(), the Base Definitions volume of
IEEE Std 1003.1-2001, <stdio.h>

COPYRIGHT
Portions of this text are reprinted and reproduced in electronic form
from IEEE Std 1003.1, 2003 Edition, Standard for Information Technology
-- Portable Operating System Interface (POSIX), The Open Group Base
Specifications Issue 6, Copyright (C) 2001-2003 by the Institute of
Electrical and Electronics Engineers, Inc and The Open Group. In the
event of any discrepancy between this version and the original IEEE and
The Open Group Standard, the original IEEE and The Open Group Standard
is the referee document. The original Standard can be obtained online
at http://www.opengroup.org/unix/online.html .

IEEE/The Open Group 2003 FGETS(3P)
------------------------------------------------------------------------------


On notera d'ailleurs que pour gets(3) il est dit :
------------------------------------------------------------------------------
APPLICATION USAGE
Reading a line that overflows the array pointed to by s results in
undefined behavior. The use of fgets() is recommended.

Since the user cannot specify the length of the buffer passed to
gets(), use of this function is discouraged. The length of the string
read is unlimited. It is possible to overflow this buffer in such a way
as to cause applications to fail, or possible system security viola‐
tions.

It is recommended that the fgets() function should be used to read
input lines.
------------------------------------------------------------------------------


D'un autre coté les pages de manuel de Linux sont pratiques pour savoir
à quel standard une fonction est conforme :
------------------------------------------------------------------------------
CONFORMING TO
C89, C99, POSIX.1-2001. LSB deprecates gets(). POSIX.1-2008 removes
the specification of gets().
------------------------------------------------------------------------------

--
Benoit Izac
Avatar
candide
> Cote description, la page de man d'OpenBSD est on ne peut plus claire,



Ben ça dépend à qui on s'adresse. Personnellement, je pense comprendre
ce qu'elle dit mais si tu prends un étudiant de Licence d'info _lambda_,
je suis sûr qu'il comprend quasiment rien.


et
rend la fonction parfaitement utilisable.




Je sais pas si tu as donné le texte complet mais il faudrait que ce
texte abstrait soit accompagné de snippets.




DESCRIPTION

The fgets() function reads at most size-1 characters from the given
stream and stores them in the string str. Reading stops when a newline
character is found, at end-of-file, or on error. The newline, if any, is
retained. The string will be NUL-terminated if fgets() succeeds; other-
wise the contents of str are undefined.

The gets() function is equivalent to fgets() with an infinite size and a
stream of stdin, except that the newline character (if any) is not stored
in the string. It is the caller's responsibility to ensure that the in-
put line, if any, is sufficiently short to fit in the string.

[nota bene: il n'est pas dit comment on peut s'assurer que la ligne en entree
va marcher pour gets(). Il n'y a pratiquement peu de cas ou l'appelant peut
assurer cette responsabilite -> gets() ne sert a rien].

RETURN VALUES
Upon successful completion, fgets() and gets() return a pointer to the
string. If end-of-file or an error occurs before any characters are
read, they return NULL. The fgets() and gets() functions do not distin-
guish between end-of-file and error, and callers must use feof(3) and
ferror(3) to determine which occurred. Whether fgets() can possibly fail
with a size argument of 1 is implementation-dependent. On OpenBSD,
fgets() will never return NULL when size is 1.




Le texte est plus friendly voire plus précis que celui de la Norme mais
il en reste assez proche.

Ce texte donne la réponse à la question que je posais à -ed- sur le
comportement de fget() lorsque son 2ème argument n vaut 1 ("un"). Le
texte de la Norme est assez difficile à interpréter sur ce point.
D'après mes essais sous Ubuntu, gcc ne renvoie pas NULL si n==1. Par
contre, dans son implémentation de fgets(), Plauger commence par

char *(fgets)(char *buf, int n, FILE *str)
{ /* get a line from stream */
unsigned char *s;

if (n<=1)
return (NULL); /* sic */

/* suite du code omise */
}


Je signale que cette question avait été déjà soulevé sur comp.std.c par
notre ami charlie :

http://groups.google.fr/group/comp.std.c/msg/b7d1d6438cb0fc1a?hl=fr


Tout cela me conduit à penser que cette fonction n'est pas si simple
même si probablement d'usage basique chez les programmeurs C "pur".

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 ?

Sinon, il est assez fréquent que les entrées soient faites non avec
fgets() mais avec fgetc() et analogues. Exemple typique, la calculatrice
en console calc (arbitrary precision calculator). Ou encore le bien
connu gnu-indent.


ferror. Et on peut considerer que c'est a la portee de n'importe qui dote
d'un cerveau en etat de fonctionner, chose un peu rare de nos jours.




:))


Une autre facon de voir, c'est de dire que ca n'a pas de sens d'enseigner
le C a des gens:
- qui n'ont pas les prerequis necessaires;
- qui sont trop cons pour comprendre (desole, c'est pas du tout
politically-correct).




Alors, il faut qu'ils soient vraiment très très cons car même moi j'ai
fini par comprendre ! :)


Du coup, c'est legitime de larguer le bagage en trop, et d'enseigner quelque
chose de correct pour les gens qui vont s'en servir en vrai.

A mon sens, le C correct est a la portee de qui s'en donne la peine. C'est
pas simple, ca demande du boulot et de l'opiniatrete. Mais il n'y a pas de
place pour les feignants qui ne feront pas les efforts necessaires, et ca ne
fait aucun sens de faire de la demagogie et d'essayer de faire croire que c'est
un langage simple et accessible au debutant sans faire d'efforts.




Tu me rassures ;)

J'ai l'impression que certaines personnes ont quand même des facilités.
Ce n'est pas trop mon cas, et le C que je comprends aujourd'hui m'a
demandé des milliers d'heures de lecture et de travail. Et quand je
vois le peu de temps que les étudiants passent à bosser, je suis en fait
pas très étonné qu'ils aient un niveau très médiocre en C.
Avatar
Benoit Izac
Bonjour,

le 07/11/2009 à 23:33, candide a écrit dans le
message <4af5f5ae$0$22505$ :

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 ?



Ceinture et bretelle ? Je n'en vois pas l'intérêt. Par contre, ce qui me
choque c'est qu'il écrive :

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] » ne sera jamais évalué.

--
Benoit Izac
Avatar
Benoit Izac
Bonjour,

le 07/11/2009 à 23:33, candide a écrit dans le
message <4af5f5ae$0$22505$ :

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 ?



Ceinture et bretelle ? Je n'en vois pas l'intérêt. Par contre, ce qui me
choque c'est qu'il écrive :

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.

--
Benoit Izac