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

Implementation correcte de strchr ?

13 réponses
Avatar
candide
Quel est votre avis sur cette implémentation de strchr ?


Le code :


/* Source à encoder en ISO-8859-1. */

#include <stdio.h>
#include <string.h>

char *mystrchr(const char *s, int c)
{
while ((unsigned char)*s != (unsigned char)c) {
if (*s == '\0')
return NULL;
++s;
}
return (char*)s;
}


int main(void)
{
const int e = getchar();
printf("%d %d\n", e, (unsigned char)e);
printf("%d %d\n", 'é', (unsigned char)'é');
printf("1 %s\n", strchr("abcdéf", 'é'));
printf("2 %s\n", strchr("abcdéf", e));
printf("3 %s\n", mystrchr("abcdéf", 'é'));
printf("4 %s\n", mystrchr("abcdéf", e));
return 0;
}

10 réponses

1 2
Avatar
Samuel DEVULDER
candide a écrit :
Quel est votre avis sur cette implémentation de strchr ?


Le code :


/* Source à encoder en ISO-8859-1. */

#include <stdio.h>
#include <string.h>

char *mystrchr(const char *s, int c)


^^^
Pourquoi int? "char" irait, non? Ca serait cohérent avec la déclaration
de "s".

C'est pas parce que strchr veut un int qu'on est obligé de faire pareil,
surtout que le manpage dit d'ailleurs que le c est converti en char en
interne (sans précision sur le signe de char).

Si quelqu'un connait la raison sur le choix du int pour c, je suis
intéressé. J'ai rien trouvé dans les manpages là dessus.

{
while ((unsigned char)*s != (unsigned char)c) {


^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
A quoi servent les deux cast en unsigned? On peut pas faire avec un cast
à (char) sur c directement? (pour que *s et (char)c aient tous les deux
le même type).

sam.
Avatar
espie
In article <4bc4e97f$0$20661$,
Samuel DEVULDER wrote:
candide a écrit :
Quel est votre avis sur cette implémentation de strchr ?


Le code :


/* Source à encoder en ISO-8859-1. */

#include <stdio.h>
#include <string.h>

char *mystrchr(const char *s, int c)


^^^
Pourquoi int? "char" irait, non? Ca serait cohérent avec la déclaration
de "s".

C'est pas parce que strchr veut un int qu'on est obligé de faire pareil,
surtout que le manpage dit d'ailleurs que le c est converti en char en
interne (sans précision sur le signe de char).

Si quelqu'un connait la raison sur le choix du int pour c, je suis
intéressé. J'ai rien trouvé dans les manpages là dessus.



EOF
Avatar
Samuel DEVULDER
Marc Espie a écrit :

Si quelqu'un connait la raison sur le choix du int pour c, je suis
intéressé. J'ai rien trouvé dans les manpages là dessus.



EOF



Heu? Mais encore? Si j'osais (arthur :-) ) je dirais que c'est du grand
n'importe quoi. EOF c'est pour les flux, et là on est dans le monde des
chaines. Il n'y a aucun rapport entre les deux.

Il doit y avoir une autre raison. Sans compter que si on passe EOF dans
c, on se retrouve à faire (char)EOF ou (unsigned char)EOF, ce qui est
d'une bêtise sans nom.

sam (EOT)
Avatar
Xavier Roche
Samuel DEVULDER a écrit :
Heu? Mais encore? Si j'osais (arthur :-) ) je dirais que c'est du grand
n'importe quoi. EOF c'est pour les flux, et là on est dans le monde des
chaines. Il n'y a aucun rapport entre les deux.



Je suppose que dans la mesure où getc() renvoi un int (pour permettre de
renvoyer des codes d'erreurs), putc() prend également un int.

Cela peut potentiellement permettre de faire en sorte que
putchar(getc()) renvoi une erreur si getc() a lui même renvoyé une erreur.

Par extension, tout ce qui manipule des char utilisent des int (je vois
aussi memset(), dans le même genre) - enfin c'est une explication
possible ; ie. strchr(str, getchar()) ne nécessite pas de cast.
Avatar
Samuel DEVULDER
Xavier Roche a écrit :
Samuel DEVULDER a écrit :
Heu? Mais encore? Si j'osais (arthur :-) ) je dirais que c'est du
grand n'importe quoi. EOF c'est pour les flux, et là on est dans le
monde des chaines. Il n'y a aucun rapport entre les deux.



Je suppose que dans la mesure où getc() renvoi un int (pour permettre de
renvoyer des codes d'erreurs), putc() prend également un int.



Oui.. que les fonction d'i/o utilisent EOF je n'ai pas de soucis, mais
c'est voir la présence de EOF dans une routine de manip de chaine qui
laisse perplexe.

Cela peut potentiellement permettre de faire en sorte que
putchar(getc()) renvoi une erreur si getc() a lui même renvoyé une erreur.



Par extension, tout ce qui manipule des char utilisent des int (je vois
aussi memset(), dans le même genre) - enfin c'est une explication
possible ; ie. strchr(str, getchar()) ne nécessite pas de cast.



Oui.. mais c'est une curieuse façon de coder. D'une part on se demande
l'intérêt d'éviter un cast à cet endroit là et de le repousser dans la
librairie. Et d'autre part la fin de fichier est silencieusement ignorée
et là c'est carrément moche. Il faut espérer que l'utilisateur fasse
appel à feof() avant de travailler avec ce que retourne strchr().

J'imagine assez qu'il y a des raisons historiques finalement là dessous.
Peut être datant de l'époque où l'on aimait pas le code machine produit
par les compilos quand il fallait convertir un int en char et qu'on
préférait gérer cela à sa sauce efficacement en asm dans les
bibliothèques standard.

sam.
Avatar
Benoit Izac
Bonjour,

le 14/04/2010 à 09:55, Samuel DEVULDER a écrit dans le message
<4bc574d9$0$20998$ :

Par extension, tout ce qui manipule des char utilisent des int (je
vois aussi memset(), dans le même genre) - enfin c'est une
explication possible ; ie. strchr(str, getchar()) ne nécessite pas
de cast.



Oui.. mais c'est une curieuse façon de coder. D'une part on se demande
l'intérêt d'éviter un cast à cet endroit là et de le repousser dans la
librairie.



D'ailleurs, à ce propos, ça donne quoi un « (char) EOF » ?

--
Benoit Izac
Avatar
Lucas Levrel
Le 14 avril 2010, Samuel DEVULDER a écrit :

Xavier Roche a écrit :
Cela peut potentiellement permettre de faire en sorte que putchar(getc())
renvoi une erreur si getc() a lui même renvoyé une erreur.

Par extension, tout ce qui manipule des char utilisent des int (je vois
aussi memset(), dans le même genre) - enfin c'est une explication possible
; ie. strchr(str, getchar()) ne nécessite pas de cast.



Oui.. mais c'est une curieuse façon de coder. D'une part on se demande
l'intérêt d'éviter un cast à cet endroit là et de le repousser dans la
librairie. Et d'autre part la fin de fichier est silencieusement ignorée et
là c'est carrément moche.



La manpage ne dit pas que l'int est converti en char immédiatement. Il
peut y avoir dans la fonction des tests d'erreur utilisant l'int, et si
tout est OK l'int est changé en char pour être recherché dans la chaîne...

--
LL
Avatar
espie
In article <4bc558cd$0$13105$,
Samuel DEVULDER wrote:
Marc Espie a écrit :

Si quelqu'un connait la raison sur le choix du int pour c, je suis
intéressé. J'ai rien trouvé dans les manpages là dessus.



EOF



Heu? Mais encore? Si j'osais (arthur :-) ) je dirais que c'est du grand
n'importe quoi. EOF c'est pour les flux, et là on est dans le monde des
chaines. Il n'y a aucun rapport entre les deux.

Il doit y avoir une autre raison. Sans compter que si on passe EOF dans
c, on se retrouve à faire (char)EOF ou (unsigned char)EOF, ce qui est
d'une bêtise sans nom.



Sois perplexe si tu veux, mais beaucoup de fonctions/macros gerent des
char ET EOF, et c'est pas forcement une mauvaise idee de renvoyer quelque
chose de sense (tel qu'un pointeur nul) si on leur passe un EOF.

Mais la vraie raison est ailleurs, c'est tout betement qu'il etait impossible
de faire autrement sans casser la compatibilite avec le C K&R.
Rappel: en C K&R, tu n'as pas de prototypes, et il y a un certain nombre
de promotions (je n'ai plus en tete le vocabulaire de la norme) qui sont
faites d'offices, tel que char -> int. Concretement, en C K&R, tu ne peux
pas passer de char a une fonction.

Du coup, mettre un char pour strchr, c'etait la rendre explicitement
incompatible avec le C classique.
Avatar
Antoine Leca
Samuel DEVULDER écrivit:
candide a écrit :
char *mystrchr(const char *s, int c)


^^^
Pourquoi int?



Pour être cohérent avec la déclaration des strchr ?
Ou pour un gain (très minime) de performance ?

"char" irait, non? Ca serait cohérent avec la déclaration de "s".



Oui (mais seulement dans le cas particulier de strchr/strrchr, à cause
de l'historique).


C'est pas parce que strchr veut un int qu'on est obligé de faire pareil,
surtout que le manpage dit d'ailleurs que le c est converti en char en
interne



Ce n'est pas non plus une raison pour inventer dans le domaine du détail
(où les gens ne font pas forcément attention).

(sans précision sur le signe de char).



Ce qui veut dire que l'on utilise le type "char" et non "signed char" ou
"unsigned char". C'est cohérent avec la règle pour l'interprétation des
constantes littérales caractères ('é'), et en particulier la conséquence
sur les machines qui ne sont pas en complément à deux, que tu ne puisses
pas « trouver » l'une des constantes -1u (en complément à 1) ou 0x80 (en
signe&magnitude, ajusté selon le nombre de bits).

Si quelqu'un connait la raison sur le choix du int pour c, je suis
intéressé.



À propos de strchr ? C'est bien entendu pour être compatible avec les
compilateurs K&R (encore qu'ici, cela ne fasse aucune différence).

C'est d'ailleurs la même raison pour le type de s : on a choisit [const]
char* uniquement par compatibilité avec l'historique K&R, sinon on
aurait choisit unsigned char *, qui serait beaucoup plus dans l'esprit
de la norme (mais qui aurait cassé beaucoup de code).


Antoine
Avatar
Benoit Izac
Bonjour,

le 14/04/2010 à 11:26, Antoine Leca a écrit dans le message
<hq41op$30f$ :

D'ailleurs, à ce propos, ça donne quoi un « (char) EOF » ?



En supposant le cas classique où EOF est codé à -1, pour la valeur :
avec char non signé, UCHAR_MAX
avec char signé, -1

Si EOF<SCHAR_MIN, cela devient du grand n'importe quoi, comydize.



Donc, on peut considérer ça comme « undefined behaviour » ?

--
Benoit Izac
1 2