OVH Cloud OVH Cloud

Cast d'une union ?

41 réponses
Avatar
Aurelien Regat-Barrel
Bonjour à tous,
Je me demandais s'il était valide de caster une union comme dans
l'exemple qui suit:

union union_t {
struct s {
short low;
short high;
} u;
long l;
};

void Example( union_t* );

void Test( long Value )
{
Example( reinterpret_cast<union_t*>( &Value ) );
}

Cet utilisation de reinterpret_cast est-elle problématique ?
Merci.

--
Aurélien Regat-Barrel

10 réponses

1 2 3 4 5
Avatar
Fabien LE LEZ
On Wed, 05 Apr 2006 14:35:03 +0200, Aurelien Regat-Barrel
:

void Test( long Value )
{
Example( reinterpret_cast<union_t*>( &Value ) );
}

Cet utilisation de reinterpret_cast est-elle problématique ?


Oui. C'est sérieusement casse-gueule, et le résultat est aléatoire.

Il y a bien plus élégant, fiable et prévisible :

void Test (long Value)// ou "(long const& Value)"
{
union_t mon_t;
mon_t.l= Value;
Example (&mon_t);
}


Par ailleurs, il me semble que le comportement de ce code est
indéfini :

union_t mon_t;
mon_t.l= quelque_chose;
std::cout << mon_t.low;

Avatar
James Kanze
Fabien LE LEZ wrote:
On Wed, 05 Apr 2006 14:35:03 +0200, Aurelien Regat-Barrel
:

void Test( long Value )
{
Example( reinterpret_cast<union_t*>( &Value ) );
}

Cet utilisation de reinterpret_cast est-elle problématique ?


Oui. C'est sérieusement casse-gueule, et le résultat est
aléatoire.

Il y a bien plus élégant, fiable et prévisible :

void Test (long Value)// ou "(long const& Value)"
{
union_t mon_t;
mon_t.l= Value;
Example (&mon_t);
}

Par ailleurs, il me semble que le comportement de ce code est
indéfini :

union_t mon_t;
mon_t.l= quelque_chose;
std::cout << mon_t.low;


Selon la norme, comportement indéfini. En practice, si les deux
accès ont lieu dans des unités de compilation (ou qu'on compile
sans optimisation), il y a de fortes chances que ça marche.

--
James Kanze


Avatar
Aurelien Regat-Barrel

void Test( long Value )
{
Example( reinterpret_cast<union_t*>( &Value ) );
}

Cet utilisation de reinterpret_cast est-elle problématique ?



Oui. C'est sérieusement casse-gueule, et le résultat est aléatoire.

Il y a bien plus élégant, fiable et prévisible :

void Test (long Value)// ou "(long const& Value)"
{
union_t mon_t;
mon_t.l= Value;
Example (&mon_t);
}


Ok. C'était pour économiser 2 lignes :-)
Cela dit, je ne comprends pas trop pourquoi c'est un problème. Pour moi
l'union c'est (entre autre) un autre moyen de faire du cast.

Par ailleurs, il me semble que le comportement de ce code est
indéfini :

union_t mon_t;
mon_t.l= quelque_chose;
std::cout << mon_t.low;


Si ton sentiment est confirmé, ça remet en cause le miens. Ce type
d'union est pas mal utilisé pour faciliter la manipulation des octets
d'une donnée quelconque. Ca voudrait dire que c'est spécifique à chaque
compilo ?

--
Aurélien Regat-Barrel


Avatar
James Kanze
Aurelien Regat-Barrel wrote:

void Test( long Value )
{
Example( reinterpret_cast<union_t*>( &Value ) );
}

Cet utilisation de reinterpret_cast est-elle problématique ?


Oui. C'est sérieusement casse-gueule, et le résultat est aléatoir e.

Il y a bien plus élégant, fiable et prévisible :

void Test (long Value)// ou "(long const& Value)"
{
union_t mon_t;
mon_t.l= Value;
Example (&mon_t);
}


Ok. C'était pour économiser 2 lignes :-)

Cela dit, je ne comprends pas trop pourquoi c'est un problème.
Pour moi l'union c'est (entre autre) un autre moyen de faire
du cast.

Par ailleurs, il me semble que le comportement de ce code est
indéfini :

union_t mon_t;
mon_t.l= quelque_chose;
std::cout << mon_t.low;


Si ton sentiment est confirmé, ça remet en cause le miens. Ce
type d'union est pas mal utilisé pour faciliter la
manipulation des octets d'une donnée quelconque.


Par qui ? C'est un comportement indéfini de lire un élément de
l'union autre que le dernier qu'on a écrit. Ça l'a toujours été,
et en général, ça ne marche pas avec beaucoup de compilateurs ;
par exemple, le compilateur voit que la valeur écrite dan
mon_t.l n'est jamais utilisée, et supprime l'écriture.

Ca voudrait dire que c'est spécifique à chaque compilo ?


Ça veut dire que ça pourrait bien marcher dans les build de
développement, mais non dans les builds de production.

--
James Kanze



Avatar
Bruno CAUSSE
dans l'article , James
Kanze à a écrit le 7/04/06 13:04 :

Par qui ? C'est un comportement indéfini de lire un élément de
l'union autre que le dernier qu'on a écrit. Ça l'a toujours été,


Ok.

Maintemenant avec nos machines a plusieurs Go a quoi servent les unions?

Avatar
Aurelien Regat-Barrel

Si ton sentiment est confirmé, ça remet en cause le miens. Ce
type d'union est pas mal utilisé pour faciliter la
manipulation des octets d'une donnée quelconque.



Par qui ? C'est un comportement indéfini de lire un élément de
l'union autre que le dernier qu'on a écrit. Ça l'a toujours été,
et en général, ça ne marche pas avec beaucoup de compilateurs ;
par exemple, le compilateur voit que la valeur écrite dan
mon_t.l n'est jamais utilisée, et supprime l'écriture.


J'ai deux exemples en tête. Un ancien, du temps de DOS, pour la
représentation des registres du CPU:

struct WORDREGS {
unsigned int ax, bx, cx, dx, si, di, cflag, flags;
};

struct BYTEREGS {
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};

union REGS {
struct WORDREGS x;
struct BYTEREGS h;
};

exemple d'utilisation:
http://perso.wanadoo.fr/olivier.lalorette/sys.htm

ou plus récent, dans le monde Windows, le type LARGE_INTEGER:

typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
LONG HighPart;
};
struct {
DWORD LowPart;
LONG HighPart; } u;
LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;

http://msdn.microsoft.com/library/en-us/winprog/winprog/large_integer_str.asp

Utilisé par SetFilePointerEx par exemple (equivalent de seek):
http://msdn.microsoft.com/library/en-us/fileio/fs/setfilepointer.asp

Le type LARGE_INTEGER est utilisé par de nombreux compilos C/C++ sous
Windows, et je pense que ça marche à chaque fois.

--
Aurélien Regat-Barrel


Avatar
Fabien LE LEZ
On Fri, 07 Apr 2006 10:44:22 +0200, Aurelien Regat-Barrel
:

Pour moi
l'union c'est (entre autre) un autre moyen de faire du cast.


Ben non. Un union est juste un moyen d'économiser de la place, quand
on sait qu'à un moment donné d'un programme, on n'utilisera qu'une
partie des membres.
En fait, "union" n'est rien d'autre qu'une optimisation. Si, en
remplaçant "union" par "struct" partout dans ton code, le sens du
programme change, il y a des chances pour que ton code de départ ait
un comportement indéfini.

Par ailleurs, quand tu écris :

union union_t {
struct s {
short low;
short high;
} u;
long l;
};

tu as l'air de penser que sizeof(union_t::s)==sizeof(long).
Y a-t-il un argument qui justifierait cela ?

Avatar
Fabien LE LEZ
On Fri, 07 Apr 2006 14:32:31 +0200, Bruno CAUSSE :

Maintemenant avec nos machines a plusieurs Go a quoi servent les unions?


Les "union", comme toute optimisation, sont utilisés quand on
s'aperçoit que le programme doit être optimisé.

Avatar
Fabien LE LEZ
On Fri, 07 Apr 2006 15:46:14 +0200, Aurelien Regat-Barrel
:

Le type LARGE_INTEGER est utilisé par de nombreux compilos C/C++ sous
Windows, et je pense que ça marche à chaque fois.


Note que windows.h est un header généralement fourni avec le compilo
(ou, au moins, testé par les développeurs de ce compilo). Par
conséquent, l'éditeur de chaque compilo s'est assuré que le cas
"LARGE_INTEGER" fonctionne.

Mais il s'agit bien sûr d'un cas où la portabilité est totalement
abandonnée.

Avatar
James Kanze
Bruno CAUSSE wrote:
dans l'article
, James
Kanze à a écrit le 7/04/06 13:04 :


Par qui ? C'est un comportement indéfini de lire un élément
de l'union autre que le dernier qu'on a écrit. Ça l'a
toujours été,



Ok.


Maintemenant avec nos machines a plusieurs Go a quoi servent
les unions?


Bonne question:-).

Je m'en sers actuellement surtout pour assurer l'alignement :

union
{
unsigned char rawMemory[ sizeof( T ) ] ;
double dummyForAlignment ;
} ;

Sinon, c'est utile (mais plutôt pour des raisons historiques)
pour les valeurs sémantiques dans yacc ou bison.

Et évidemment, il y a bien des programmes qui traitent des
ensembles de données de plusieurs milliards d'éléments. Parfois
en flottant, parfois en fixe. Alors, en attendant avoir la
mémoire qui correspond à l'adressage 64 bits... (Note bien que
quand tu traites de très grands tableaux comme ça, plus tu tiens
en mémoire réele, moins ton programme swappe. Ce qui peut être
important aussi.)

--
James Kanze
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


1 2 3 4 5