OVH Cloud OVH Cloud

Un exemple de cast utile sans pointeur

53 réponses
Avatar
Marc Boyer
Je me permets de vous demander un brin d'aide, encore.

Je cherche un exemple simple où la conversion par
cast est nécessaire. Je trouve rien sans devoir
introduire des pointeurs...

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(

10 réponses

2 3 4 5 6
Avatar
Jean-Marc Bourguet
"Antoine Leca" writes:

Jean-Marc Bourguet va escriure:
Comme c'est le seul point que je pensais faire, je me demande ce que
tu me disputes.


Utiliser char (et non pas int) comme type pour les variables.


Ah, le fait que c'etait "char c; ... c" et pas "char* p; ... *p" etait
tout a fait insubstantiel a mon exemple (qui concernait les casts
utiles sans pointeur, je ne crois pas que ce soit cet utilisation de
pointeur qui genait Marc, sinon on peut aussi indexer un tableau de
char).

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Marc Boyer
Tout cela n'est pas encore très clair pour moi...

Antoine Leca wrote:
En c107e3$pid$, Marc Boyer va escriure:
Antoine Leca wrote:

if (isprint((unsigned char)c)) {


Au secours ! Transtypage indéfini !


Mais bon, imaginons le code
char c=rand()?'x':'1';

1) est-ce que ce code est bon ?


Défini « bon »
Correct : oui.


C'est déjà pas mal.

2) comment je peux tester que c'est un chiffre
avec isdigit ?


Bon, si tu as « typé » c en char, il faut effectivement, comme l'as écrit
Jean-Marc, transtyper en unsigned char. Sur ce point, il as parfaitement
raison, je ne le lui ai jamais disputé cela.


Je cherche à comprendre pourquoi ne pas le transtyper serait faux.
Ca vient du fait qu'on a pas l'assurance que sizeof(int)>sizeof(char),
et que donc la promotion de c (éventuellement non signée) ne peut
pas se faire de façon fiable en int si sizeof(int)==sizeof(char) ?
Ou l'erreur est ailleurs ?

Parce que typer en char, ça a quand même l'avantage d'être
compatible avec les chaines char*, sinon ça repousse
juste le problème, non ?

Pour ma culture, 'ÿ', c'est une constante de quel type ?


int (en C ; char en C++, c'est une différence entre les deux).


C'est là que mon K&R2d induit en erreur ($2.3) en passant
sous silence le type de 'x'.

Mais bon, les caractères, c'est quoi finalement en C ?


Norme C (ici N869)

3.5 character
bit representation that fits in a byte


Donc, dans la norme 'character <=> char || unsigned char ||
signed char'.
Et les constantes comme 'x', on les nomme comment ?
Mon K&R parle de 'character constant'...

Ce qui nous mène à

3.4 byte
addressable unit of data storage large enough to hold any member of
the basic character set of the execution environment


En fait, les char comme petit entier, je crois que ça va,
mais c'est les 'x', 'ÿ' qui me troublent assez profondément.

Et ce machin (le basic bidule chouette environment), c'est défini par
l'implémentation, mais cela doit contenir tous les caractères imprimables
ASCII (sauf @ $ `), avec des représentations positives de surcroît. Tu auras
peut-être déduit que 'ÿ' peut donc avoir une représentation négative (c'est
la cas qu'évoquait Jean-Marc). Donc pour en revenir à ton exemple, avec ma
correction, si tu veux utiliser 'ÿ' à la place de ton 'x', et ensuite
pouvoir utiliser isdigit(), il faut écrire

int c = rand() ? (unsigned char)'ÿ' : '1';


Là, je ne comprends plus: 'ÿ' est un entier. On peut
donc l'assigner à c sans perte d'information aucune.
Ensuite, isdigit prend un int en paramêtre. OK, je
lis dans la doc qu'il faut que cet int ait la valeur
d'un unsigned char...
C'est le bordel quand même, non ?

L'arbre des types de ce bout de code est un poëme :
'ÿ' est un int, éventuellement négatif
(unsigned char)'ÿ' est un caractère non signé, donc positif
il est promu en int par (et pour) l'opération ? :
'1' est un int
le résultat de ? : est donc un int, pas de problème à ce niveau
on initialise un int avec un int, pas de problème non plus; notons c est
toujours positif, et peut-être passé à isdigit()

Dans le cas du code
char c = rand() ? 'ÿ' : '1';

'ÿ' est un int, éventuellement négatif (si char est signé)
'1' est un int (positif)
le résultat de ? : est donc un int, positif ou négatif
on initialise un char avec un int, il y a conversion; pas de changement de
signe intempestif cependant.
Si char est signé et 'ÿ' est encodé en Latin-1, c contient -1; si tu le
passes à isdigit(), tu as perdu.


Parce que isdigit attends un int qui soit l'encodage d'un
unsigned char et pas d'un signed char, c'est ça ?

Et si tu passes n'importe quel autre
caractère avec un encodage négatif, tu dois compter sur le bon vouloir de
l'implémentation pour te donner le comportement attendu (le comportement est
indéfini d'après la norme).


Bon, et maintenant que puis-je expliquer à des débutants
sans avoir à leur expliquer toutes les subtilités ?

1) si vous manipuler uniquement des caractères ASCII et
pas de fichiers, tout est simple, les caractères
sont des char
2) si vous manipuler uniquement des caractères ASCII et
des fichiers avec getc[har], vous devez d'abord vous
assurer que le retour est correct (soit en le stockant
dans un int et en le comparant à EOF, soit en le
stockant dans un char et en testant feof et ferror)
puis vous pouvez le mettre dans un char.
3) si vous travaillez dans la vraie vie où les caractères
peuvent avoir des accents, vous devez vous assurer
que les retour de getc[har] codent bien un caractère
(c'est à dire, soit différent de EOF), puis vous
pouver le stocker dans un char, mais l'utilisation
des isXXX demande qu'on écrive
isXXX( (unsigned char) param)


Dernière question, getc[har] ne pouvait pas retourner
EOF, est-ce qu'on aurait pas pu simplement lui faire
retourner un char, et avoir des isXXXX qui prennent
un char (ou unsigned char) en paramêtre ?

Désolé d'être long, mais j'aimerais comprendre
(et savoir ce que je vais raconter).

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(




Avatar
Antoine Leca
En c11unp$5t0$, Marc Boyer va escriure:
Tout cela n'est pas encore très clair pour moi...


Ne t'inquiètes pas, tu progresses.
Mais c'est vrai que ces notions ne sont pas évidentes à saisir, surtout
qu'en plus je mélange (involontairement) les messages à destination des
débutants et les messages à destination des programmeurs confirmés.

Il y a un point que je pense tu dois commencer à entrevoir, c'est qu'il ne
faut pas mélanger char et "character". C'est très différent aujourd'hui en
C.

Comme tu l'as écrit, char n'est rien d'autre que le plus petit type entier,
et en même temps « l'unité de compte » (que l'on appelle "byte").

De leur côté, les caractères ne sont pas clairement défini dans le modèle de
programmation du langage, et c'est bien là la racine de tout le problème.

Historiquement, les caractères étaient uniquement ceux du jeu ASCII, leur
représentation était unique et invariable, ils étaient tous positifs,
entraient dans des variables de type char, et tout allait bien. Aucune de
ces quatre affirmations n'est vrai en général aujourd'hui (et quand on relis
cela, cela laisse réveur que cela puisse même fonctionner...)


2) comment je peux tester que c'est un chiffre
avec isdigit ?


Bon, si tu as « typé » c en char, il faut effectivement, comme l'as
écrit Jean-Marc, transtyper en unsigned char. Sur ce point, il as
parfaitement raison, je ne le lui ai jamais disputé cela.


Je cherche à comprendre pourquoi ne pas le transtyper serait faux.


Problème de mathématique !
Le domaine de la fonction isdigit() est l'ensemble des valeurs de type char,
plus EOF. Comme on veut une injection, il faut que EOF soit disjoint des
valeurs assignés aux caractères. La convention, c'est que EOF est négatif,
et historiquement (c'est-à-dire presque partout) égal à (-1). Donc la
définition de la fonction impose que les valeurs des caractères qui sont
passées à isdigit(), soient positives (ou nulles).

De l'autre côté, pour des raisons historiques, char peut être un type signé.
Donc pour passer un caractère, stocké dans un "char", à isdigit(), tu dois
le transtyper.
Si tu le stockes dans un int, pas de problème, c'est directement bon, à
condition d'avoir pris la précaution de stocker la valeur positive dans la
variable int.

Ca vient du fait qu'on a pas l'assurance que sizeof(int)>sizeof(char),


En fait, la contrainte sur isdigit() est la meilleure preuve que
sizeof(int) > sizeof(char) [qui vaut 1].

Parce que typer en char, ça a quand même l'avantage d'être
compatible avec les chaines char*, sinon ça repousse
juste le problème, non ?


Parfaitement exact. Si tu types en int, et si tu as des chaînes en char*, il
faut transtyper (en unsigned char) au moment où tu extrais les caractères de
la chaîne.

Tu es en bon chemin, là.


Et les constantes comme 'x', on les nomme comment ?
Mon K&R parle de 'character constant'...


Exact. Mais j'ai passé sous silence hier parce que c'est beaucoup moins
clair...


6.4.4.4 Character constants

[2] An integer character constant is a sequence of one or more multibyte
characters enclosed
in single-quotes, as in 'x' or 'ab'.

Semantics
[10] An integer character constant has type int. The value of an integer
character constant
containing a single character that maps to a member of the basic execution
character set is
the numerical value of the representation of the mapped character
interpreted as an
integer. The value of an integer character constant containing more than one
character, or
containing a character or escape sequence not represented in the basic
execution character
set, is implementation-defined.


Note: tu peux oublier pour le moment le fait d'avoir plusieurs caractères,
ainsi que la notion de "multibyte"; comme le dis la dernière phrase, de
toute manières c'est défini par l'implémentation.

Comme tu peux le voir, c'est tout sauf une définition simple et limpide.


int c = rand() ? (unsigned char)'ÿ' : '1';


Là, je ne comprends plus: 'ÿ' est un entier. On peut
donc l'assigner à c sans perte d'information aucune.


Sauf que comme ÿ est codé supérieur à 128, (signed char)'ÿ' et (unsigned
char)'ÿ' ont deux valeurs différentes¹. Par définition, 'ÿ' est égal à
(char)'ÿ', et vaut l'une ou l'autre des deux valeurs. Comme on veut être sûr
que la valeur stocké dans la variable soit positive, on est obligé de
transtyper.

Là, normalement, on se demande, mais pourquoi la norme ne dit pas que 'ÿ'
soit forcément la valeur positive ? la réponse, comme souvent, c'est la
nécessité de la compatibilité historique. Parce que les Américains, au tout
début, sur le fameux PDP11, utilisaient seulement l'ASCII, et donc D.
Ritchie a choisi de faire que char soit signé sur le premier compilateur C.
Dès le deuxième compilateur, qui devait pouvoir fonctionner sur une machine
EBCDIC (où '0' se code 240), le type char a été transformé en non signé.
C'était il y a 28 ans. Depuis, c'est le souk.

C'est le bordel quand même, non ?


On est d'accord.

__________
¹: sauf si CHAR_BIT ne vaut pas 8, mais c'est un cas théorique sans intérêt
ici.
¯¯¯¯¯¯¯¯¯¯



Bon, et maintenant que puis-je expliquer à des débutants
sans avoir à leur expliquer toutes les subtilités ?

1) si vous manipuler uniquement des caractères ASCII et
pas de fichiers, tout est simple, les caractères
sont des char
2) si vous manipuler uniquement des caractères ASCII et
des fichiers avec getc[har], vous devez d'abord vous
assurer que le retour est correct (soit en le stockant
dans un int et en le comparant à EOF, soit en le
stockant dans un char et en testant feof et ferror)
puis vous pouvez le mettre dans un char.
3) si vous travaillez dans la vraie vie où les caractères
peuvent avoir des accents, vous devez vous assurer
que les retour de getc[har] codent bien un caractère
(c'est à dire, soit différent de EOF), puis vous
pouver le stocker dans un char, mais l'utilisation
des isXXX demande qu'on écrive
isXXX( (unsigned char) param)


C'est trop compliqué.

1) Les caractères se stockent dans des int. Il n'y a aucun avantage, et
plutôt des inconvénients, à utiliser des variables char isolées. Le type
char est réservé aux chaînes de caractères, via les dérivés char[] et char*.

2) les fonctions de fichiers s'interfacent directement; pour voir s'il y a
une erreur, on compare avec EOF (qui est la seule valeur négative que peut
renvoyer fgetc et consorts)

3) idem avec isdigit() et compagnie; de plus, ces derniers se comportent «
bien » quand on leur founit EOF, et plus généralement le résultat de fgetc
et consorts

4) pour stocker un caractère (dans un int, donc) vers une chaîne char*, on
assigne directement [et oui, je sais que c'est défini par l'implémentation,
6.3.1.3p3]

5) pour extraire un caractère depuis une chaîne, il faut un transtypage en
unsigned char. Sinon un jour ou l'autre on aura le problème que signalait
Jean-Marc. C'est le seul point avec une difficulté.
ÀMHA, bien sûr.

6) si on manipule beaucoup de caractères accentués, on a avantage quand
c'est possible:
- soit à utiliser tout le temps unsigned char * à la place de char *
- soit à utiliser une option du compilateur pour forcer char à être non
signé. C'est moins dans l'esprit de la portabilité parce que cela va laisser
des problèmes le jour où le code devra fonctionner avec des char signés;
mais c'est plus simple à programmer.

Corrollaire du dernier point:
7) si on écrit une bibliothèque ou plus généralemnt du code destiné à être
réutilisé par quelqu'un d'autre, faire que dans son propre environnement
char soit signé et tester son code avec des caractères accentués; si c'est
bon dans ce cas-là, cela va aussi l'être dans les autres cas moins
problématiques. Notez que cela ne signifie pas obliger les utilisateurs de
la bibliothèque à utiliser des char signés.


Dernière question, getc[har] ne pouvait pas retourner
EOF, est-ce qu'on aurait pas pu simplement lui faire
retourner un char, et avoir des isXXXX qui prennent
un char (ou unsigned char) en paramêtre ?


Oui. Si tu continues dans ce chemin-là, tu obtiens Ada.

Bon, je me moques. Bien sûr, si getc pouvait signaler les cas d'erreur avec,
au hasard, une exception, dans le cas normal il pourrait renvoyer un char,
et fin du problème. Mais ce n'est plus (du tout) le même langage, et donc tu
perds la compatibilité avec tout le code qui existe, et qui a
fondamentalement besoin que les cas d'erreur en lecture soit indiqué par la
valeur de retour EOF (ou plus généralement une valeur négative) de la
fonction getc.
Le langage C est un compromis, ce n'est jamais parfait. Défendre C comme
étant « meilleur » est presque toujours une mauvaise option. En plus, comme
c'est maintenant un vieux langage (les ordinateurs électroniques ont 60 ans
cette année, C en a plus de 30 !), certains choix conceptuels sont
archaïques. Il faut faire avec.


Antoine



Avatar
Marc Boyer
Antoine Leca wrote:
En c11unp$5t0$, Marc Boyer va escriure:
Tout cela n'est pas encore très clair pour moi...


Ne t'inquiètes pas, tu progresses.
Mais c'est vrai que ces notions ne sont pas évidentes à saisir, surtout
qu'en plus je mélange (involontairement) les messages à destination des
débutants et les messages à destination des programmeurs confirmés.

Il y a un point que je pense tu dois commencer à entrevoir, c'est qu'il ne
faut pas mélanger char et "character". C'est très différent aujourd'hui en
C.


J'avais compris que c'était différent (d'où ma question
sur le type de 'x'), mais pas vraiment ce que chacun était.

2) comment je peux tester que c'est un chiffre
avec isdigit ?


Bon, si tu as « typé » c en char, il faut effectivement, comme l'as
écrit Jean-Marc, transtyper en unsigned char. Sur ce point, il as
parfaitement raison, je ne le lui ai jamais disputé cela.


Je cherche à comprendre pourquoi ne pas le transtyper serait faux.


Problème de mathématique !
Le domaine de la fonction isdigit() est l'ensemble des valeurs de type char,
plus EOF. Comme on veut une injection, il faut que EOF soit disjoint des
valeurs assignés aux caractères. La convention, c'est que EOF est négatif,
et historiquement (c'est-à-dire presque partout) égal à (-1). Donc la
définition de la fonction impose que les valeurs des caractères qui sont
passées à isdigit(), soient positives (ou nulles).


OK, alors que s'il avait été égal à INT_MIN, INT_MAX ou quoi que
ce soit en dehors de CHAR_MIN...CHAR_MAX, on était sauvé.

Si tu le stockes dans un int, pas de problème, c'est directement bon, à
condition d'avoir pris la précaution de stocker la valeur positive dans la
variable int.


OK

Ca vient du fait qu'on a pas l'assurance que sizeof(int)>sizeof(char),


En fait, la contrainte sur isdigit() est la meilleure preuve que
sizeof(int) > sizeof(char) [qui vaut 1].


J'imaginais un monde ou, par exemple, char faisait 16 bits,
mais où les "characters constants" auraient été dans
un intervalle de taille 2^15. On gardait la place pour coder
EOF en dehors (ça laissait de la marge).

Parce que typer en char, ça a quand même l'avantage d'être
compatible avec les chaines char*, sinon ça repousse
juste le problème, non ?


Parfaitement exact. Si tu types en int, et si tu as des chaînes en char*, il
faut transtyper (en unsigned char) au moment où tu extrais les caractères de
la chaîne.


Transtyper en unsigned char ? A oui, sinon, 'ÿ' se retrouve en EOF
souvent...

6.4.4.4 Character constants

[2] An integer character constant is a sequence of one or more multibyte
characters enclosed in single-quotes, as in 'x' or 'ab'.

Semantics
[10] An integer character constant has type int. The value of an integer
character constant
containing a single character that maps to a member of the basic execution
character set is
the numerical value of the representation of the mapped character
interpreted as an
integer. The value of an integer character constant containing more than one
character, or
containing a character or escape sequence not represented in the basic
execution character
set, is implementation-defined.


Note: tu peux oublier pour le moment le fait d'avoir plusieurs caractères,
ainsi que la notion de "multibyte"; comme le dis la dernière phrase, de
toute manières c'est défini par l'implémentation.

Comme tu peux le voir, c'est tout sauf une définition simple et limpide.


Voui.

int c = rand() ? (unsigned char)'ÿ' : '1';


Là, je ne comprends plus: 'ÿ' est un entier. On peut
donc l'assigner à c sans perte d'information aucune.


Sauf que comme ÿ est codé supérieur à 128, (signed char)'ÿ' et (unsigned
char)'ÿ' ont deux valeurs différentes¹. Par définition, 'ÿ' est égal à
(char)'ÿ', et vaut l'une ou l'autre des deux valeurs. Comme on veut être sûr
que la valeur stocké dans la variable soit positive, on est obligé de
transtyper.


A cause du conflit de EOF. Car s'il était encodé en dehors des
valeurs des "character constant", que ce soit positif ou négatif,
on s'en moquerait, car celui qui fournir les isXXXX est aussi
celui qui décide le signe de char, donc il a juste a être cohérent
avec lui même.
Non ?

Là, normalement, on se demande, mais pourquoi la norme ne dit pas que 'ÿ'
soit forcément la valeur positive ? la réponse, comme souvent, c'est la
nécessité de la compatibilité historique. Parce que les Américains, au tout
début, sur le fameux PDP11, utilisaient seulement l'ASCII, et donc D.
Ritchie a choisi de faire que char soit signé sur le premier compilateur C.
Dès le deuxième compilateur, qui devait pouvoir fonctionner sur une machine
EBCDIC (où '0' se code 240), le type char a été transformé en non signé.
C'était il y a 28 ans. Depuis, c'est le souk.


Voui, ce que j'ai dit aux élèves: le jour ou on a normalisé,
il y avait des compilo signés, des compilos non signés, et ils
on pas pu (voulu) trancher.

Bon, et maintenant que puis-je expliquer à des débutants
sans avoir à leur expliquer toutes les subtilités ?

1) si vous manipuler uniquement des caractères ASCII et
pas de fichiers, tout est simple, les caractères
sont des char
2) si vous manipuler uniquement des caractères ASCII et
des fichiers avec getc[har], vous devez d'abord vous
assurer que le retour est correct (soit en le stockant
dans un int et en le comparant à EOF, soit en le
stockant dans un char et en testant feof et ferror)
puis vous pouvez le mettre dans un char.
3) si vous travaillez dans la vraie vie où les caractères
peuvent avoir des accents, vous devez vous assurer
que les retour de getc[har] codent bien un caractère
(c'est à dire, soit différent de EOF), puis vous
pouver le stocker dans un char, mais l'utilisation
des isXXX demande qu'on écrive
isXXX( (unsigned char) param)


C'est trop compliqué.

1) Les caractères se stockent dans des int. Il n'y a aucun avantage, et
plutôt des inconvénients, à utiliser des variables char isolées. Le type
char est réservé aux chaînes de caractères, via les dérivés char[] et char*.

2) les fonctions de fichiers s'interfacent directement; pour voir s'il y a
une erreur, on compare avec EOF (qui est la seule valeur négative que peut
renvoyer fgetc et consorts)

3) idem avec isdigit() et compagnie; de plus, ces derniers se comportent «
bien » quand on leur founit EOF, et plus généralement le résultat de fgetc
et consorts

4) pour stocker un caractère (dans un int, donc) vers une chaîne char*, on
assigne directement [et oui, je sais que c'est défini par l'implémentation,
6.3.1.3p3]

5) pour extraire un caractère depuis une chaîne, il faut un transtypage en
unsigned char. Sinon un jour ou l'autre on aura le problème que signalait
Jean-Marc. C'est le seul point avec une difficulté.

ÀMHA, bien sûr.


Je sais que c'est non-optimum, mais je crois que je préfère
ma version 3).
char c;
c= getchar() + test feof && ferror
isXXXX( (unsigned char) param )

Il me semble que
a) ça prolonge un rien l'illusion que les char sont des
caractères (déjà que l'affectation comme opérateur,
j'ai failli en perdre)
b) ça localise les transtypages à l'usage de fonctions
particulères
c) ça reste correct (si j'ai bien compris)

6) si on manipule beaucoup de caractères accentués, on a avantage quand
c'est possible:
- soit à utiliser tout le temps unsigned char * à la place de char *


Et les str* de string.h fonctionnent encore ?

- soit à utiliser une option du compilateur pour forcer char à être non
signé. C'est moins dans l'esprit de la portabilité parce que cela va laisser
des problèmes le jour où le code devra fonctionner avec des char signés;
mais c'est plus simple à programmer.

Corrollaire du dernier point:
7) si on écrit une bibliothèque ou plus généralemnt du code destiné à être
réutilisé par quelqu'un d'autre, faire que dans son propre environnement
char soit signé et tester son code avec des caractères accentués; si c'est
bon dans ce cas-là, cela va aussi l'être dans les autres cas moins
problématiques. Notez que cela ne signifie pas obliger les utilisateurs de
la bibliothèque à utiliser des char signés.


Je note surtout le conseil des caractères accentué.
J'ai pas pour but de leur faire écrire une bibliothèque.

Dernière question, getc[har] ne pouvait pas retourner
EOF, est-ce qu'on aurait pas pu simplement lui faire
retourner un char, et avoir des isXXXX qui prennent
un char (ou unsigned char) en paramêtre ?


Oui. Si tu continues dans ce chemin-là, tu obtiens Ada.

Bon, je me moques. Bien sûr, si getc pouvait signaler les cas d'erreur avec,
au hasard, une exception, dans le cas normal il pourrait renvoyer un char,
et fin du problème. Mais ce n'est plus (du tout) le même langage, et donc tu
perds la compatibilité avec tout le code qui existe, et qui a
fondamentalement besoin que les cas d'erreur en lecture soit indiqué par la
valeur de retour EOF (ou plus généralement une valeur négative) de la
fonction getc.


Sans passer par une exception, on pouvait imaginer
un idiome
char c;
if ( (c=getc()) && !feof )
en lieu et place de
int c;
if ( (c=getc()) != EOF )

mais le jour ou on a du y penser, il devait être trop tard.

Le langage C est un compromis, ce n'est jamais parfait. Défendre C comme
étant « meilleur » est presque toujours une mauvaise option. En plus, comme
c'est maintenant un vieux langage (les ordinateurs électroniques ont 60 ans
cette année, C en a plus de 30 !), certains choix conceptuels sont
archaïques. Il faut faire avec.


Mon cours commence avec:
- le C est un vieux langage (1970), moderne à l'époque...

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(




Avatar
Gabriel Dos Reis
"Antoine Leca" writes:

| Comme tu peux le voir, c'est tout sauf une définition simple et limpide.

Autrement cen ne serait plus du C (ou C++ si tu avais un doute).

[...]

| C'était il y a 28 ans. Depuis, c'est le souk.

You shall pay for your father and grand-father, il dit le mec.

| > C'est le bordel quand même, non ?
|
| On est d'accord.

<nod>

[...]

| > Dernière question, getc[har] ne pouvait pas retourner
| > EOF, est-ce qu'on aurait pas pu simplement lui faire
| > retourner un char, et avoir des isXXXX qui prennent
| > un char (ou unsigned char) en paramêtre ?
|
| Oui. Si tu continues dans ce chemin-là, tu obtiens Ada.

ou std::char_traits<> en C++ ;-)

| Bon, je me moques. Bien sûr, si getc pouvait signaler les cas d'erreur avec,
| au hasard, une exception, dans le cas normal il pourrait renvoyer un char,
| et fin du problème. Mais ce n'est plus (du tout) le même langage, et donc tu
| perds la compatibilité avec tout le code qui existe, et qui a
| fondamentalement besoin que les cas d'erreur en lecture soit indiqué par la
| valeur de retour EOF (ou plus généralement une valeur négative) de la
| fonction getc.
| Le langage C est un compromis, ce n'est jamais parfait. Défendre C comme
| étant « meilleur » est presque toujours une mauvaise option. En plus, comme
| c'est maintenant un vieux langage (les ordinateurs électroniques ont 60 ans
| cette année, C en a plus de 30 !), certains choix conceptuels sont
| archaïques. Il faut faire avec.

« archaïques » n'est pas le terme que j'utiliserais. Je pense
fortement à « restrict ».

-- Gaby
Avatar
Antoine Leca
En c12cg6$93g$, Marc Boyer va escriure:
Antoine Leca wrote:
Le domaine de la fonction isdigit() est l'ensemble des valeurs de
type char, plus EOF. Comme on veut une injection, il faut que EOF
soit disjoint des valeurs assignés aux caractères. La convention,
c'est que EOF est négatif, et historiquement (c'est-à-dire presque
partout) égal à (-1). Donc la définition de la fonction impose que
les valeurs des caractères qui sont passées à isdigit(), soient
positives (ou nulles).


OK, alors que s'il avait été égal à INT_MIN, INT_MAX ou quoi que
ce soit en dehors de CHAR_MIN...CHAR_MAX, on était sauvé.


Aurait été.


En fait, la contrainte sur isdigit() est la meilleure preuve que
sizeof(int) > sizeof(char) [qui vaut 1].


J'imaginais un monde ou, par exemple, char faisait 16 bits,
mais où les "characters constants" auraient été dans
un intervalle de taille 2^15. On gardait la place pour coder
EOF en dehors (ça laissait de la marge).


En fait, ton exemple existe réellement: le domaine de ISO 10646 (au départ)
était prévu pour coder tous les caractères que l'on puisse inventer. Ils ont
pensé (avec raison) que 16 bits, ce ne serait pas assez, ne serait-ce que
parce qu'il existe des jeux de caractères (chinois) qui dépasse les 100 000,
avec il est vrai des doublons.
Donc ils ont prévu un espace de 32 bits. Et ils ont spécifié, dès le départ,
que seuls les caractères positifs pouveient être encodés, ce qui en laisse 2
milliard.
Ce beau modèle n'a pas survecu à un simple constat : chaque bout de texte en
Europe ou aux États-Unix quadruple de taille, donc la taille de la mémoire
de la plupart des ordinateurs (qui en général manipule du texte, jusqu'à des
dates récentes) devait quadrupler d'un seul coup. Les consomateurs
potentiels ont dit, « non ».

Donc on revient sur terre, et char est limité à 8 bits.

Et de toutes manières, la nature a horreur du vide. Donc si on invente des
types char aussi grand que l'on veut, on aura des GU (gentils utilisateurs)
pour trouver des utilisations pour tous les bits utilisables...


Transtyper en unsigned char ? A oui, sinon, 'ÿ' se retrouve en EOF
souvent...


Voilà, tu as compris.

int c = rand() ? (unsigned char)'ÿ' : '1';
Comme on veut être sûr que la valeur stocké dans la variable soit


positive, on est obligé de transtyper.


A cause du conflit de EOF.


Oui.

Car s'il était encodé en dehors des
valeurs des "character constant", que ce soit positif ou négatif,
on s'en moquerait, car celui qui fournir les isXXXX est aussi
celui qui décide le signe de char, donc il a juste a être cohérent
avec lui même.


C'est cela. Comme « celui qui fournit isxxx » ne connait pas forcément celui
qui l'appelle (c'est tout le propos des bibliothèques), ils se mettent
d'accord sur une interface, en l'occurence une norme. Qui dit que les
caractères doivent être positifs.

6) si on manipule beaucoup de caractères accentués, on a avantage
quand c'est possible:
- soit à utiliser tout le temps unsigned char * à la place de char
*


Et les str* de string.h fonctionnent encore ?


Oui.
D'un côté, ceux qui ne regardent pas au détail (strlen, strcpy, qui ne se
préoccupent que de savoir si cahque caractère est nul ou pas) marchent de la
même façon, la norme a un paragraphe spécial qui assure que les encodages de
char*, signed char* et unsigned char* sont équivalents.
De l'autre côté, ceux qui s'en préoccupent, genre strcmp, sont spécifiés en
terme de unsigned char !!! (et bien sûr, encore une fois, le protoype char*,
c'est historique)


Je note surtout le conseil des caractères accentué[s].
J'ai pas pour but de leur faire écrire une bibliothèque.


Non bien sûr. Ce paragraphe est plutÔt destiné aux autres lecteurs (s'il en
y a encore qui suivent...)


Mon cours commence avec:
- le C est un vieux langage (1970), moderne à l'époque...


1972. Si tu lis l'anglais, la référence est à
<URL:http://cm.bell-labs.com/cm/cs/who/dmr/primevalC.html>


Antoine




Avatar
Marc Boyer
Antoine Leca wrote:
En c12cg6$93g$, Marc Boyer va escriure:
Voilà, tu as compris.


Je garde juste cette citation, hors contexte,
par pur plaisir ;-)

Mon cours commence avec:
- le C est un vieux langage (1970), moderne à l'époque...


1972. Si tu lis l'anglais, la référence est à
<URL:http://cm.bell-labs.com/cm/cs/who/dmr/primevalC.html>


Voui.

Bon, sinon, tu ne m'as pas repris sur ce que j'allais
leur raconter (stockage dans des char et test par feof et
ferror + transtypage pour les isXXX), j'en déduis que
c'est correct.

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(


Avatar
Jean-Marc Bourguet
Marc Boyer writes:

Antoine Leca wrote:
En c12cg6$93g$, Marc Boyer va escriure:
Voilà, tu as compris.


Je garde juste cette citation, hors contexte,
par pur plaisir ;-)

Mon cours commence avec:
- le C est un vieux langage (1970), moderne à l'époque...


1972. Si tu lis l'anglais, la référence est à
<URL:http://cm.bell-labs.com/cm/cs/who/dmr/primevalC.html>


Voui.

Bon, sinon, tu ne m'as pas repris sur ce que j'allais
leur raconter (stockage dans des char et test par feof et
ferror + transtypage pour les isXXX), j'en déduis que
c'est correct.


Il y a un probleme si une erreur de lecture a lieu, sinon si je
comprends bien la norme feof() n'est vrai apres un fgetc)_ que si la
lecture a echoue. Note que ce n'est pas le cas en general pour les
autres fonctions de lecture apres lesquelles feof() peut etre vrai
alors que la lecture a reussi (mais la suivante echouera). Ce qui est
vraissemblablement la raison fondamentale pour laquelle on n'utilise
pas cette structure.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
Marc Boyer
Jean-Marc Bourguet wrote:
Marc Boyer writes:
Bon, sinon, tu ne m'as pas repris sur ce que j'allais
leur raconter (stockage dans des char et test par feof et
ferror + transtypage pour les isXXX), j'en déduis que
c'est correct.


Il y a un probleme si une erreur de lecture a lieu, sinon si je
comprends bien la norme feof() n'est vrai apres un fgetc)_ que si la
lecture a echoue. Note que ce n'est pas le cas en general pour les
autres fonctions de lecture apres lesquelles feof() peut etre vrai
alors que la lecture a reussi (mais la suivante echouera). Ce qui est
vraissemblablement la raison fondamentale pour laquelle on n'utilise
pas cette structure.


Je suis pas sur de te suivre.
Il me semble que l'idiome des E/S en C, c'est qu'il
faut échouer dans une lecture pour s'appercevoir qu'on
est à la fin, et dans ce cadre, je ne vois pas la distinction
entre getc[har] et fgets par exemple.

J'ai raté un truc ?

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(


Avatar
Jean-Marc Bourguet
Marc Boyer writes:

Jean-Marc Bourguet wrote:
Marc Boyer writes:
Bon, sinon, tu ne m'as pas repris sur ce que j'allais
leur raconter (stockage dans des char et test par feof et
ferror + transtypage pour les isXXX), j'en déduis que
c'est correct.


Il y a un probleme si une erreur de lecture a lieu, sinon si je
comprends bien la norme feof() n'est vrai apres un fgetc)_ que si la
lecture a echoue. Note que ce n'est pas le cas en general pour les
autres fonctions de lecture apres lesquelles feof() peut etre vrai
alors que la lecture a reussi (mais la suivante echouera). Ce qui est
vraissemblablement la raison fondamentale pour laquelle on n'utilise
pas cette structure.


Je suis pas sur de te suivre.
Il me semble que l'idiome des E/S en C, c'est qu'il
faut échouer dans une lecture pour s'appercevoir qu'on
est à la fin, et dans ce cadre, je ne vois pas la distinction
entre getc[har] et fgets par exemple.

J'ai raté un truc ?


Non. Enfin, l'antecedant de "cette structure" vraissemblablement.
C'est la structure de

char c;
if ( (c=getc()) && !feof )
en lieu et place de
int c;
if ( (c=getc()) != EOF )

mais le jour ou on a du y penser, il devait être trop tard.


Ou tu fais l'operation sans tester sa validite (en fait il y a
probleme si getc() renvoie 0 que je n'avais pas signale -- dans ton
message tu as aussi donne une description plus abstraite et je
travaillais de memoire) et tu testes ensuite feof (et ferror dans ton
autre description). Pour getc (hormis le pb de 0 et ferror), je ne
crois pas qu'il y ait un probleme. Les autres operations (fgets par
exemple) peuvent mettre feof (et peut-etre ferror faudrait que je
regarde) sans echouer.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org



2 3 4 5 6