OVH Cloud OVH Cloud

cast sur pointeur

14 réponses
Avatar
JBB
char c;
unsigned char uc = c;

Ceci compile très bien.

char * pc ;
unsigned char * puc = pc;

Et la ça ne compile plus !!
Pourtant ça ne me parait pas tellement différent.
(dans la mesure ou les 2 types pointés font la même taille).

Du coup je suis obligé de faire un cast.
unsigned char * puc = (unsigned char *) pc;
a la mode C ou
unsigned char * puc = reinterpret_cast<unsigned char *>(pc);
a la mode C++
car
unsigned char * puc = static_cast<unsigned char *>(pc);
ne fonctionne pas non plus.

Du coup je suis obligé d'avoir dans mon code, un cast qui donne
l'impression que je fais une grosse bidouille alors qu'en fait non (en
tout cas pas plus que dans le 1er exemple).

Que cela vous inspire t'il ?

10 réponses

1 2
Avatar
Marc Boyer
Le 31-01-2006, JBB a écrit :
char c;
unsigned char uc = c;

Ceci compile très bien.

char * pc ;
unsigned char * puc = pc;

Et la ça ne compile plus !!
Pourtant ça ne me parait pas tellement différent.
(dans la mesure ou les 2 types pointés font la même taille).


int i= 0;
float f= i;

Ca compile.

int * pi ;
float * pf = pi ;

ne compile pas, alors que sur ma machine, ils
ont la même taille.

assert( sizeof(int) == sizeof(float) );

Du coup je suis obligé d'avoir dans mon code, un cast qui donne
l'impression que je fais une grosse bidouille alors qu'en fait non (en
tout cas pas plus que dans le 1er exemple).

Que cela vous inspire t'il ?


Que ce n'est pas parce que deux objets sont convertibles
implicitement l'un en l'autre et qu'ils ont la même taille
qu'ils ont la même représentation interne.

Ceci dit, en effet, dans le cas particulier de char,
on pourrait discuter, surtout dans le sens char -> unsigned char
puisqu'il n'y a pas sur unsigned char les hypothétiques
'trap value'.

Marc Boyer
--
Entre le fort et le faible, c'est la liberte qui opprime et le droit
qui libere. Henri Lacordaire, Dominicain

Avatar
kanze
JBB wrote:
char c;
unsigned char uc = c;

Ceci compile très bien.


Parce qu'il y a une conversion implicite entre les deux types.

char * pc ;
unsigned char * puc = pc;

Et la ça ne compile plus !!
Pourtant ça ne me parait pas tellement différent.
(dans la mesure ou les 2 types pointés font la même taille).


Il y a quand même une grande différence. Un unsigned char n'est
pas un char. Dans le premier cas, il y a eu une conversion
(potentiellement avec changement de valeur !), ce qui a donné un
nouvel objet ; ce nouvel objet a ensuite servi de valeur
d'initialisation de l'objet uc. Ce qui importe, c'est que c et
uc sont deux objets différents, avec deux valeurs indépendantes.

Dans le deuxième cas, tu as un pointeur à un char. Et non à un
unsigned char. L'objet pointé ne peut pas changer de type ;
c'est donc normal qu'il n'y a pas de conversion implicite.

Du coup je suis obligé de faire un cast.


Du coup, à ta place, je me démanderais si je n'étais pas en
train de faire une bêtise.

unsigned char * puc = (unsigned char *) pc;
a la mode C ou
unsigned char * puc = reinterpret_cast<unsigned char *>(pc);
a la mode C++
car
unsigned char * puc = static_cast<unsigned char *>(pc);
ne fonctionne pas non plus.


Justement. Parce que la conversion est dangéreuse, et le
résultat non portable.

Du coup je suis obligé d'avoir dans mon code, un cast qui
donne l'impression que je fais une grosse bidouille alors
qu'en fait non (en tout cas pas plus que dans le 1er exemple).

Que cela vous inspire t'il ?


Les deux cas me semble nettement différents. Je n'aime pas trop
que la première conversion soit implicite, mais le résultat en
est bien défini, et il y a des cas où elle est tout à fait
valide. Dans le deuxième, il s'agit d'un cas de ce qu'on appelle
en anglais « type punning ». Tu mens au compilateur. Et c'est
très important -- tu ne convertis pas un char en unsigned char ;
tu accèdes à un objet de type char comme s'il était un unsigned
char. C'est jouer avec le feu, et il y a bien peu de cas où ça
se justifie.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Fabien LE LEZ
On 1 Feb 2006 00:36:43 -0800, "kanze" :

Du coup, à ta place, je me démanderais si je n'étais pas en
train de faire une bêtise.


Il faut effectivement se poser la question. Mais j'ai rencontré des
cas où ça se justifie plus ou moins. Par exemple, une fonction de
lecture de bitmap qui écrit dans un char[], et la fonction d'affichage
à l'écran veut un "unsigned char const*".
En fait, c'est souvent un "void*" qu'il aurait fallu.

Avatar
kanze
Marc Boyer wrote:

[...]
Que ce n'est pas parce que deux objets sont convertibles
implicitement l'un en l'autre et qu'ils ont la même taille
qu'ils ont la même représentation interne.


Par définition, prèsque, signed char et unsigned char ne peuvent
pas avoir la même représentation interne. Signed char a
obligatoirement un bit de signe, unsigned char ne peut pas en
avoir un.

Dans la plupart des implémentations que je connais, char
ordinaire a la même représentation que signed char.

Ceci dit, en effet, dans le cas particulier de char,
on pourrait discuter, surtout dans le sens char -> unsigned char
puisqu'il n'y a pas sur unsigned char les hypothétiques
'trap value'.


Ce qu'il faudrait, c'est un type « byte », pour représenter la
mémoire brute. A priori, il aurait la même représentation
interne que unsigned char, mais il accepterait les conversions
de pointeur selon les mêmes règles que void, et pour
l'utilisateur, il signalerait bien que conceptuellement, on veut
accéder à la mémoire brute, et non à de petits entiers
non-signés. (Je ne sais pas s'il faudrait que byte soit un type
entier, avec les opérations arithmétiques.)

Le problème avec la conversion char* -> unsigned char*, c'est
justement qu'il n'y a pas de conversion de l'objet pointé. Si on
le fait pour accéder à la mémoire brute, c'est ce qu'on veut. Si
on le fait avec l'idée d'accéder à des valeurs, on risque
d'avoir des surprises -- de toute façon, le résultat ne serait
pas portable. (-1 donne habituellement 255, par exemple, mais
sur un Unisys 2200, il risque de donner 510. Tandis que
convertir un char en unsigned char sur ce processeur donnera
511.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
JBB
char c;
unsigned char uc = c;

Ceci compile très bien.

char * pc ;
unsigned char * puc = pc;

Et la ça ne compile plus !!
Pourtant ça ne me parait pas tellement différent.
(dans la mesure ou les 2 types pointés font la même taille).


int i= 0;
float f= i;

Ca compile.

int * pi ;
float * pf = pi ;

ne compile pas, alors que sur ma machine, ils
ont la même taille.

assert( sizeof(int) == sizeof(float) );

Effectivement avec un double ca marche beaucoup moins bien:

int i = 1;
double d = i;
int * ptr_i = &i;
double * ptr_d = (double *) ptr_i;
cout << d << " =? " << *ptr_d << end;

Dans mon cas ca revient au même uniquement parce que un char et un
unsigned ont la même représentation en mémoire.

Du coup je suis obligé d'avoir dans mon code, un cast qui donne
l'impression que je fais une grosse bidouille alors qu'en fait non (en
tout cas pas plus que dans le 1er exemple).

Que cela vous inspire t'il ?


Que ce n'est pas parce que deux objets sont convertibles
implicitement l'un en l'autre et qu'ils ont la même taille
qu'ils ont la même représentation interne.

Ceci dit, en effet, dans le cas particulier de char,
on pourrait discuter, surtout dans le sens char -> unsigned char
puisqu'il n'y a pas sur unsigned char les hypothétiques
'trap value'.

Marc Boyer



Avatar
kanze
Fabien LE LEZ wrote:
On 1 Feb 2006 00:36:43 -0800, "kanze" :

Du coup, à ta place, je me démanderais si je n'étais pas en
train de faire une bêtise.


Il faut effectivement se poser la question. Mais j'ai
rencontré des cas où ça se justifie plus ou moins. Par
exemple, une fonction de lecture de bitmap qui écrit dans un
char[], et la fonction d'affichage à l'écran veut un "unsigned
char const*".

En fait, c'est souvent un "void*" qu'il aurait fallu.


C'est à dire que quelqu'un d'autre a déjà fait la bêtise:-).

Sérieusement, il y a effectivement des cas où le
reinterpret_cast se justifie -- sinon, il faudrait se démander
pourquou il est là. Normalement, ils ne concernent que des
logiciels de très bas niveau, où ne travaille que les
spécialistes. Mais comme tu dis, on a parfois aussi affaire à
des interfaces externes auxquelles il faut s'adapter.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Fabien LE LEZ
On 1 Feb 2006 09:36:50 -0800, "kanze" :

C'est à dire que quelqu'un d'autre a déjà fait la bêtise:-).


Ben... Quand on travaille sur des bitmaps, ça se justifie plus ou
moins :

On travaille sur des blocs de 8 bits -- chacun représentant une
composante d'un pixel -- sans se préoccuper de la valeur numérique
associée. On lit des bits (8 par 8) dans un fichier, et on les balance
à la carte vidéo ; c'est le processeur de la carte vidéo qui s'occupe
de transformer tout ça en luminosité.
On utilise donc le type "entier à 8 bits" "par défaut" : char.

Sauf que parfois, assez rarement[*], on veut effectuer des opérations
arithmétiques dessus. Du coup, on s'intéresse à leur valeur, de 0 à
255, et on passe en "unsigned char".


[*] Assez rarement en C++ -- et moins rarement en assembleur.

Avatar
kanze
Fabien LE LEZ wrote:
On 1 Feb 2006 09:36:50 -0800, "kanze" :

C'est à dire que quelqu'un d'autre a déjà fait la bêtise:-).


Ben... Quand on travaille sur des bitmaps, ça se justifie plus
ou moins :


Quand on travaille sur des bitmaps, unsigned char est tout à
fait indiqué. Mais d'où vient le problème ? Si on n'a que des
unsigned char (et des unsigned char*), il n'y a pas besoin de
conversion. C'est que quelqu'un a utiliser char quand il ne
convenait pas.

On travaille sur des blocs de 8 bits -- chacun représentant
une composante d'un pixel -- sans se préoccuper de la valeur
numérique associée. On lit des bits (8 par 8) dans un fichier,


Avec les flux standard ? C'est là la « bêtise », alors -- les
flux standard utilise char pour la mémoire brute, plutôt que
unsigned char. En fait, la distinction entre des caractères et
des bytes de mémoire ou de données brutes est un peu flou dans
les flux. Mais on pourrait bien arguer qu'avec l'utilisation de
la facette codecvt dans filebuf, les flux ne conviennent que
pour des caractères.

Ce qui est tout assez théorique. Dans la pratique, ça serait une
bêtise de ne pas utiliser les flux s'ils conviennent, pour des
raisons de portabilité, ou simplement pour ne pas réinventer la
roue. De point de vue pragmatique, alors, l'utilisation des deux
types, et la conversion explicite, c'est le moindre mal.

et on les balance à la carte vidéo ; c'est le processeur de la
carte vidéo qui s'occupe de transformer tout ça en luminosité.
On utilise donc le type "entier à 8 bits" "par défaut" : char.


J'utiliserais par défaut ici unsigned char. Dans la mésure où je
ne m'intéresse pas à la valeur numérique, c'est ce qui convient
le mieux, AMHA. Mais en fait, dans ce cas, c'est réelement une
valeur numérique, dans l'intervale [0,255]. Ce qui veut dire
qu'il n'y a que unsigned char qui convient (à moins d'utiliser
un type avec plus de 8 bits).

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Fabien LE LEZ
On 2 Feb 2006 01:39:47 -0800, "kanze" :

Dans la pratique, ça serait une
bêtise de ne pas utiliser les flux s'ils conviennent, pour des
raisons de portabilité, ou simplement pour ne pas réinventer la
roue.


J'ai eu de gros problèmes de performances avec les fstream. Mais ça
vient peut-être du fait que mon compilo est obsolète : avec FILE*, je
ne suis pas très loin d'obtenir la vitesse des fonctions de l'API
Win32.

Avatar
Fabien LE LEZ
On 2 Feb 2006 01:39:47 -0800, "kanze" :

Quand on travaille sur des bitmaps, unsigned char est tout à
fait indiqué.


Mais la plupart des fonctions utilisées ne savent pas qu'il s'agit de
bitmaps.

En fait, sauf dans les cas particuliers où on s'intéresse à la valeur
numérique des pixels (pour faire des opérations arithmétiques), le
type le plus logique serait void* : on transfère un gros bloc de
mémoire du fichier vers la RAM, puis vers la mémoire vidéo.

1 2