Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

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

1 2 3 4 5
Avatar
Samuel Devulder
a écrit :
Bonjour

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

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

A quoi correspond la dernière position (numero 5) ?



A rien de spécial. Probablement ce qu'il y avait dans le buffer à cette
case là avant. De toute façon ton exemple est suspect...

Si tu as saisi ta chaîne au clavier, tu devrais avoir le caractère de
fin de ligne dans le buffer. Et il ne figure pas dans ton exemple.

Autre possibilité, tu lis un fichier binaire qui contient '' à
l'offset 4 et '???' à l'offset 5. Bref ton fgets() est allé lire plus
loin que tu ne crois. En effet fgets() lit jusqu'à la fin de ligne et
'' n'est pas une fin de ligne et peut donc être lu comme tout caractère.

Peux tu préciser la façon dont tu utilises fgets()?


(En fait, je veux dire que je dois décrémenter 2 fois apres la fin
de la boucle for pour pointer p2 sur 'd' en position 3). Ca veut dire
qu'en plus de '', fgets inclue un autre caractère.



C'est pas ça le soucis. C'est le fait que tu doives faire p2[-2] pour
pointer sur 'd'. En toute logique avec ton code qui sépare le *p2 et le
p2++, à la sortie p2 *doit* pointer sur le , et donc 'd' est en
p2[-1]. Après si tu as écris ta boucle for ainsi:
for(p2 = ...; *p2++; ) { ... }
(i.e. tu teste *p2 et tu incrémente p2 dans la foulée, indépendamment du
résultat du test). Alors à la sortie de la boucle p2 est allé un pas
plus loin que le ''. Dans ce cas là, et ce cas là uniquement, 'd' est
accessible via p2[-2] (-1 : on pointe sur '', -2 : on pointe sur 'd').

Maintenant une autre possibilité si tu l'indiques, tu sépares
effectivement le *p2 et le p2++ (ou ++p2 suivant ce qu'on préfère)
c'est que tu modifie p2 à l'intérieure de la boucle et que tu sorte
d'elle via un break ou un goto. Bref un truc pas très propre.


Question supplémentaire (culture général en informatique) :
La chaine enregistrée avec fgets se trouve dans la memoire ram ou
dans un des registres?



Dans fgets() on doit passer un buffer de stockage.

(man fgets)
#include <stdio.h>
char *fgets(char *BUF, int N, FILE *FP);

Donc la question ne se pose pas.

En outre j'ai du mal à imaginer un buffer de longeur variable stocké
dans les registres du CPU. A part quelques CPU exotiques auquels on peut
acceder aux registres via un tableau (i.e. ils sont accessibles de façon
indéxée via un autre registre), je ne vois pas trop le moyen pratique
pour faire cela. Quant à l'intérêt, il est nul. Les registres sont plus
utiles aux calculs qu'à stocker des valeurs. Pour stocker des valeurs la
RAM suffi amplement, et si on craint les temps accès il faut comprendre
qu'avec fgets() on fait des entrées sortie donc des temps d'un ordre de
grandeur infiniment supérieur aux accès mémoire.

sam.
Avatar
Samuel Devulder
Samuel Devulder a écrit :

Si tu as saisi ta chaîne au clavier, tu devrais avoir le caractère de
fin de ligne dans le buffer. Et il ne figure pas dans ton exemple.

Autre possibilité, tu lis un fichier binaire qui contient '' à
l'offset 4 et '???' à l'offset 5. Bref ton fgets() est allé lire plus
loin que tu ne crois. En effet fgets() lit jusqu'à la fin de ligne et
'' n'est pas une fin de ligne et peut donc être lu comme tout caractère.



Une autre option est que tu lise un fichier se terminant par abcd (sans
retour à la ligne).. et dans ce cas fgets() ajoute un '' final.

Peux tu préciser la façon dont tu utilises fgets()?



Ca reste d'actualité.

sam.
Avatar
bpascal123
On Nov 3, 7:51 pm, Samuel Devulder
wrote:
Samuel Devulder a écrit :

> Si tu as saisi ta chaîne au clavier, tu devrais avoir le caractère de
> fin de ligne dans le buffer. Et il ne figure pas dans ton exemple.

> Autre possibilité, tu lis un fichier binaire qui contient '' à
> l'offset 4 et '???' à l'offset 5. Bref ton fgets() est allé lire pl us
> loin que tu ne crois. En effet fgets() lit jusqu'à la fin de ligne et
> '' n'est pas une fin de ligne et peut donc être lu comme tout carac tère.

Une autre option est que tu lise un fichier se terminant par abcd (sans
retour à la ligne).. et dans ce cas fgets() ajoute un '' final.

> Peux tu préciser la façon dont tu utilises fgets()?

Ca reste d'actualité.

sam.



Grand merci,

Voici la cause de mes questions :

int main(void)
{
char Ch[101] ;
char *p1, *p2 ;
int Pali ;

p2 = Ch ;


printf("nEntrez une ligne de texte (max.100 caract.) : n") ;
fgets(Ch, 101, stdin) ;

for ( p2 ; *p2 ; p2++ )
;

p2 = p2-2 ;

...

La même chose avec gets :

printf("Entrez une ligne de texte (max.100 caractères) :n");
gets(CH);
/* Placer P2 sur la dernière lettre de la chaîne */
for (P2=CH; *P2; P2++)
;

P2--;


voila.

Merci
Avatar
Manuel Pégourié-Gonnard
scripsit :

(En fait, je veux dire que je dois décrémenter 2 fois apres la fin
de la boucle for pour pointer p2 sur 'd' en position 3). Ca veut dire
qu'en plus de '', fgets inclue un autre caractère.



Je crois que cette information est donnée dans la première réponse de
Samuel : c'est le caractère de fin de ligne 'n'.

Du coup, ce qu'on a c'est :

0 = a
1 = b
2 = c
3 = d
4 = n
5 =

Quand ta boucle s'arrête, *p vaut , *(p-1) vaut n et c'est fort
logiquement *(p-2) qui vaut 'd'.


--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
Samuel Devulder
a écrit :

fgets(Ch, 101, stdin) ;

for ( p2 ; *p2 ; p2++ )
;



Ok.. donc stdin et pas de bizzareries dans la boucle for. J'imagine que
tu termines ton entrée avec un "return" plutot qu'un ctrl-d (ou ctrl-z
sous win/dos). Dans ce cas comme indiqué par Manuel le buffer doit
contenir: {'a', 'b', 'c', 'd', 'n', ''}, et donc 'd' se trouve 2
places avant le '' pointé par p2, d'où le:


p2 = p2-2 ;




La même chose avec gets :



Oui! gets() mange le 'n' final.. et donc le buffer contient {'a', 'b',
'c', 'd', ''}, et p2 pointe 1 place après le 'd', d'où la nécéssité de
ne reculer que d'une place:

P2--;



En fait fgets() ne nettoie pas le caractère de fin de ligne alors que
gets() si.

sam.
Avatar
-ed-
On 3 nov, 18:39, ""
wrote:
Bonjour

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



Comment le sais-tu ?

0 = a
1 = b
2 = c
3 = d
4 =



Correct

5 = ???



La valeur est indéfinie. Mais p ne devraot pointer sur cet emplacement
mémoire.


A quoi correspond la dernière position (numero 5) ?



A rien.

(En fait, je veux dire que je dois décrémenter 2 fois apres la fin
de la boucle for pour pointer p2 sur 'd' en position 3). Ca veut dire
qu'en plus de '', fgets inclue un autre caractère.



Non. Mais il est probable que la saisie avec fgets de "abcd", suivie
de l'appui de la touche ENTER donne en réalité :

'a','b','c','d','n',0

C'est probablement ça qui te trouble.

http://www.bien-programmer.fr/notes.php#saisie

Question supplémentaire (culture général en informatique) :
La chaine enregistrée avec fgets se trouve dans la memoire ram ou
dans un des registres?



Elle se trouve en mémoire. C'est tout ce qu'on peut dire. Selon la
façon dont la variable a été définie, elle sera en mémoire
automatique, statique ou allouée.

http://www.bien-programmer.fr/notes.php#donnees

Nota : la notion de mémoire est plus abstraite (mémoire 'logique') que
la notion de RAM (mémoire physique).
Avatar
-ed-
On 3 nov, 21:39, Samuel Devulder
wrote:

En fait fgets() ne nettoie pas le caractère de fin de ligne alors que
gets() si.



En fait, gets() ne 'nettoie' pas. En fait, il le lit avec getc() (il
faut bien déterminer quelle est la fin de la saisie ...), puis le
replace dans le stdin avec ungetc().

Ce qui fait que la saisie suivante n'est pas bloquante.

de toutes façons, il ne faut pas utiliser getc() qui est un bug.
Avatar
candide
-ed- a écrit :


En fait, gets() ne 'nettoie' pas. En fait, il le lit avec getc() (il
faut bien déterminer quelle est la fin de la saisie ...), puis le
replace dans le stdin avec ungetc().



Pure spéculation. Tu sais ce que c'est que l'"encapsulation" ?

En outre, Plauger écrit (page 291):

Other functions have logic that parallels fgetc but avoids calling it in
the interest of speed. One is fread, (...). Two others are fgets and gets.

Et quand on regarde son code de gets, on ne lit aucun appel à getc, il
n'y a que des appels à des fonctions privées de bas niveau.


de toutes façons, il ne faut pas utiliser getc() qui est un bug.




gets() est un bug tu veux dire ?
C'est ce que j'ai toujours entendu partout en sorte que je ne sais même
pas ce qu'elle fait. Donc je viens de regarder et je trouve que c'est
exactement la fonction dont nombre de débutants rêvent disposer.


Combien de fois ai-je lu cette plainte :

"je comprends pas pourquoi mais scanf lit le jour mais pas le mois ?
Comment je fais ?"

?

Réponse :

1°) scanf et printf sont les colifichets de la programmation bling-bling.
2°) Utilise gets().


:))



Illustration :

#include <stdio.h>

int main(void)
{
char t[100];

gets(t);
printf("%sn", t);

return 0;
}



:~$ c99 fclc.c
/tmp/ccyhtwXO.o: In function `main':
fclc.c:(.text+0x23): warning: the `gets' function is dangerous and
should not be used.
:~$ ./x
4 novembre 2009
4 novembre 2009
:~$
Avatar
-ed-
On 4 nov, 18:45, candide wrote:
-ed- a écrit :



> En fait, gets() ne 'nettoie' pas. En fait, il le lit avec getc() (il
> faut bien déterminer quelle est la fin de la saisie ...), puis le
> replace dans le stdin avec ungetc().

Pure spéculation. Tu sais ce que c'est que l'"encapsulation" ?



C'est décrit comme ça dans le K&R ...

> de toutes façons, il ne faut pas utiliser getc() qui est un bug.

gets() est un bug tu veux dire ?



Oui, erreur de frappe.

C'est ce que j'ai toujours entendu partout en sorte que je ne sais même
pas ce qu'elle fait. Donc je viens de regarder et je trouve que c'est
exactement la fonction dont nombre de débutants rêvent disposer.



En théorie, oui, mais il manque un paramètre essentiel : la taille
maximale du tableau de char. La saisie ne peut donc être évitée, et
c'est un trou de sécurité bien connu et imparable.

Combien de fois ai-je lu cette plainte :

"je comprends pas pourquoi mais scanf lit le jour mais pas le mois ?
Comment je fais ?"



On apprend à utiliser fgets().

Réponse :

1°) scanf et printf sont les colifichets de la programmation bling-blin g.



Pff... tu te répètes et ça n'a aucun intérêt...

2°) Utilise gets().



Faux. Inciter un débutant à utiliser un trou de sécurité est soit d e
l'ignorance soit du sabotage.

#include <stdio.h>

int main(void)
{
  char t[100];

  gets(t);



et si je tapes 100 caractères, il se passe quoi ? UB. Tout peut
arriver.

  printf("%sn", t);

  return 0;

}

:~$ c99 fclc.c
/tmp/ccyhtwXO.o: In function `main':
fclc.c:(.text+0x23): warning: the `gets' function is dangerous and
should not be used.



Tiens tiens ! Jamais tu lis les messages ?
Avatar
-ed-
On 4 nov, 11:46, -ed- wrote:
de toutes façons, il ne faut pas utiliser getc() qui est un bug.



gets() ...
1 2 3 4 5