[info C] getchar ne get pas du tout un char

getchar ne get pas du tout un char [info C] - C++ - Programmation

Marsh Posté le 14-10-2002 à 03:34:26    

Aïe aïe aïe...
J'ai voulu savoir comment il fallait vraiment manipuler getchar, et une maison m'est tombée sur la tête !
 
Tout ce qui suit s'applique à:

Code :
  1. getc    (FILE*);
  2. getchar (     );
  3. fgetc   (FILE*);
  4. fgetchar(     ); //pas standard
  5. getch   (     ); //pas standard
  6. getche  (     ); //pas standard


 
 
Niveau 1
Il suffit de lire la documentation pour remarquer que le type de retour est int, et non pas char.
La raison est historique: à l'époque de ces fonctions antédilluviennes, toutes les fonctions renvoyaient le seul type existant: int.
Les types plus petits qu'un int étant promus en int pour la transmission, il n'y avait pas grand mal.
Et puis cela permettait de signaler les erreurs...
 
 
Niveau 2
Puisqu'un int dispose de plus de valeurs qu'un char, on en a profité pour spécifier une valeur de retour spéciale:
EOF, qui indique qu'une erreur est survenue, ou que la fin de l'entrée est atteinte.
Ce n'est pas un caractère, sa valeur n'est celle d'aucun caractère.
Une conséquence directe est que ceci est bogué:

Code :
  1. char c; //devrait être int
  2. while((c= getchar()) != EOF) //bogue
  3. /*...*/;


En effet, on compare à EOF non pas la valeur renvoyée par getchar, mais la valeur contenue dans c après affectation.
EOF n'étant pas représentable dans un char, ce code ne marche pas. Du moins ne devrait pas, certains compilateurs le faisant fonctionner à tort.
La solution est simple: c doit être un int.
Malheureusement, il y a pire...
 
 
Niveau 3
Le standard spécifie que le type char est distinct de signed char et unsigned char, et que chaque implémentation(1) est libre de le faire se comporter comme l'un ou l'autre. C'est pour leur permettre de choisir ce qui se manipule le mieux, et qui est le plus rapide.
Seulement, getchar & co. renvoient un code de caractère toujours positif, assimilable à un unsigned char. Probablement une raison historique...
Une conséquence directe est que ceci est bogué (bis):

Code :
  1. char c= getchar();  //bogue

Si char est non-signé, aucun problème.
Si char est signé, alors quoi ?
L'affectation est orientée sur la valeur arithmétique, et la préserve quand c'est possible.
Les règles exactes sont:

Citation :

Si la valeur source peut être représentée dans le type de destination, elle est inaltérée.
Autrement, si le type de destination est non-signé, la valeur est réduite via modulo U<type>_MAX+1.
Autrement le type de destination est signé et la valeur est dépendante de l'implémentation.

Donc, si la valeur renvoyéee par getchar est trop grande, le résultat est dépendant... Il peut même y avoir un signal de levé.
Dans la plupart des cas, les bits sont simplement copiés, mais ça n'est absolument pas garanti.
En particulier, il peut ne pas y avoir une correspondance réversible entre valeurs des version signed et unsigned d'un type:

Code :
  1. unsigned char uc1= getchar(); //ignorons EOF
  2. /**/     char  c = uc1; //signé ?
  3. unsigned char uc2= c ;
  4. if(uc1==uc2); //peut être faux !
  5. if(uc1== (unsigned char)(char)uc1); //peut être faux !


Votre documentation est censée mentionner quelque part quelle transformation est effectuée. Sincèrement, je doute qu'elle le fasse.
 
C'est bien beau tout ça, mais alors on fait comment ?
 
 
Niveau 4
Ce qui est toujours bogué (ter):

Code :
  1. #include <limits.h>
  2. int i= getchar();
  3. char c1= i; //louvoiement inutile
  4. char c2= (char)i; //cast redondant
  5. char c3= i+CHAR_MIN; //arithmetique (optimisable)
  6. char c4= *(char *)&i; //aproximation du reinterpret_cast<char>(i) de C++


Stupides:
c1: L'int intermédiaire ne fait que retarder le problème.
c2: Le cast en char a de toutes façons lieu à l'affectation, c'est lui qui transforme la valeur.
 
c3: Pas mal.
Ça donne bien une valeur dans toute la gamme d'un char. C'est même optimisé si char est non-signé.
Mais... ce n'est pas forcément la valeur attendue par putchar, celle qui afficherait le caractère qui a été entré.
 
c4: Bien essayé...
Malheureusement, le codet(2) récupéré dépend du boutisme(3), ce n'est pas forcément celui qui nous intéresse.
 
 
Niveau 5
Ce qui devrait marcher:

Code :
  1. #include <limits.h>
  2. #include <stdio.h>
  3. #define EOF2 (CHAR_MIN-1)
  4. int getcharforreal(void){
  5. int i= getchar();
  6. if(i == EOF)
  7.  return EOF2;
  8. {
  9.  unsigned char uc = i; //écrétage des octets en trop
  10.  char c= *(char*)&uc; //réprésentation binaire intacte
  11.  return c; //cast en int
  12. }
  13. }


Et encore... Les conversions entre pointeurs de types différents ne sont pas garanties de marcher (sauf void*). Je ne sais pas si ça concerne même les variantes de signes...
 
EOF ne convient pas comme valeur de retour, car bien que ce ne soit pas une valeur de unsigned char, ça pourrait être valeur de char signé. D'ailleurs, ç'est très souvent -1.
 
 
Conclusion
Ça fait peur, non ?
 
En fait, il y a très peu de chances que tout ceci ai une quelconque importance pour vous.
On imagine mal un compilateur foirer un truc aussi simple que la lecture de caractères.
Mais, telle une épée de Damoclès, ça peut vous tomber dessus un jour.
 
A noter que les formats "%c" et "%s" de scanf et fscanf ne doivent logiquement pas souffrir de ce problème.
En effet, ils ignorent le type du pointeur reçu, à cause de la liste d'argument variable, et écrivent une représentation binaire sans même pouvoir se soucier de valeur arithmétique.
 
 
Cas du C++

Code :
  1. cin.get() ; //équivalent à getchar()
  2. cin.get(c); //ok
  3. cin >> c  ; //ok


 
 
(1)implémentation:
Quasiment synonyme de compilateur.
 
(2)codet:
Mot français pour byte.
L'octet fait spécifiquement 8 bits, ce qui n'est pas toujours le cas du byte.
Le char du C++ défini comme un byte.
Donc, codet. Ou multiplet.
 
(3)boutisme:
Mot français pour endianness.
L'ordre dans lequel les octets d'une valeur sont placés en mémoire.
Autrement dit: par quel bout commence-t'on ?
Il existe aussi un boutisme pour l'ordre des bits.
 
Voilà, la visite guidée est terminée, je vous remercie de m'avoir suivi dans les tréfonds de la machinerie du C.


Message édité par Musaran le 14-10-2002 à 23:24:02

---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Marsh Posté le 14-10-2002 à 03:34:26   

Reply

Marsh Posté le 21-11-2002 à 17:47:31    

g une tite question sur le niveau2 : char c = getchar()
 
si getchar retourne EOF, quelle valeur va prendre c ? ou p-e cela va-t-il faire une erreur ?
 
je lis la suite ...
 
c super interressant je trouve comme sujet, les trucs ou tlm passe dessus sans faire attention mais il y a tellement de choses a dire dessus, j'adore ces questions "bas niveau" :)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 21-11-2002 à 17:48:41    

oups j'aurais p-e du lire la suite ... je reposerais ma question a la fin si elle a tjs lieu ... en tt cas super sujet :)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 21-11-2002 à 17:51:56    

si, ma question reste alors ...


Message édité par blackgoddess le 21-11-2002 à 17:52:55

---------------
-( BlackGoddess )-
Reply

Marsh Posté le 21-11-2002 à 18:02:42    

(dsl, g ecrit juste ce message pour etre notifier par mail)
(je savais pas comment faire en editant)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 21-11-2002 à 18:22:28    

BlackGoddess a écrit a écrit :

(dsl, g ecrit juste ce message pour etre notifier par mail)
(je savais pas comment faire en editant)




 
Il te suffit de cliquer là :  
http://forum.hardware.fr/email.php [...] ic=&theme=


---------------
brisez les rêves des gens, il en restera toujours quelque chose...  -- laissez moi troller sur discu !
Reply

Marsh Posté le 21-11-2002 à 18:44:37    

merci :)


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 21-11-2002 à 20:55:35    

Je vois pas où est le probleme si on a bien compris que les valeures signées c pas des valeures non signées :sweat:

Reply

Marsh Posté le 22-11-2002 à 00:44:43    

Je pensais pas qu'il remonterait celui-là...
 

BlackGoddess a écrit a écrit :

g une tite question sur le niveau2 : char c = getchar()
 
si getchar retourne EOF, quelle valeur va prendre c ? ou p-e cela va-t-il faire une erreur ?



 
Si char est non-signé, il subit un modulo CHAR_MAX+1.
Cela ferait probablement -1%256= 255 dans ce contexte.
 
Si char est signé, c'est dépendant... Le plus simple, c'est de conserver la même représentation de bits.
En complément à deux sur 8 bits, cela transforme -1 en 255.
 
Cela ne déclenchera pas d'erreur d'après le standard.
 
Il faut se souvenir de ces histoires de calculs sur types différents.
Comme c'est assez courant, les compilateurs ne sont pas bien bavards là-dessus, et les erreurs de portabilité sont faciles à faire.


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Marsh Posté le 22-11-2002 à 16:25:38    

et les erreurs de portabilité sont faciles à faire.
 
bin une erreur c tjs facile a faire lol :p


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 22-11-2002 à 16:25:38   

Reply

Marsh Posté le 22-11-2002 à 23:21:35    

Ouais d'accord...
J'aurais dû dire qu'elles sont pas faciles à trouver.
Ça te vas ?


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Marsh Posté le 23-11-2002 à 21:37:23    

hey jme moquais pas !!! c t ironique j'avais tres bien compris !


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 24-11-2002 à 03:03:50    

Mais non, c'est vrai ! :pfff:
 
Cette mauvaise formulation trahit une pensée confuse, pas assez bien formée et mal maitrisée.  :jap:
Et même fausse: certains se donnent beaucoup de mal pour exploiter ces opportunités de bogues. :pt1cable:
 
La vérité est qu'elles sont difficiles à repérer puisqu'il faut changer de plate-forme pour qu'elles surgissent (peut-être).
C'est d'ailleurs un test recommandé.
 
 
 :na: Faut pas chercher la petite bête avec moi... :ange:  


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
Reply

Marsh Posté le 24-11-2002 à 04:05:46    

Musaran a écrit a écrit :

Mais non, c'est vrai ! :pfff:
 
Cette mauvaise formulation trahit une pensée confuse, pas assez bien formée et mal maitrisée.  :jap:
Et même fausse: certains se donnent beaucoup de mal pour exploiter ces opportunités de bogues. :pt1cable:
 
La vérité est qu'elles sont difficiles à repérer puisqu'il faut changer de plate-forme pour qu'elles surgissent (peut-être).
C'est d'ailleurs un test recommandé.
 
 
 :na: Faut pas chercher la petite bête avec moi... :ange:  




Maintenant on sait qu'il a une petite b...ête. :D
 :o  
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed