Fio,
C'est vrai au sens général, mais puisqu'on sait ce qu'on veut faire, interpréter les données d'un unsigned int comme si c'était un float, le comportement est parfaitement défini. :)
Version imprimable
Je me range un peu à l'avis de Jean-Marc: à mon avis, ça fonctionnera toujours sans aucune optimisation, mais vu que les compilateurs optimisent de plus en plus agressivement, il est possible qu'ils finissent par faire capoter ce genre d'astuces. A moins d'utiliser "volatile"?
Corrector, Jean-Marc, quelle serait alors selon vous (je ne connais pas assez bien le standard) la façon correcte et conforme au standard pour faire ce type de cast? Car pour l'instant nous n'avons répondu que par la négative...
Carl
Dans ma perception de risque croissant:
- memcpy
- reinterpret_cast<T*>
- union
Je ne vois pas de raisons pour faire foirer la première méthode si interpréter les bits comme l'autre type a du sens.
La deuxième va poser des problèmes si les contraintes d'alignement sont différentes entre les deux types, mais à part cela je ne vois pas non plus de raison de casser cela.
La troisième n'a pas ce problème, mais est celle qui risque le plus de n'être pas supportée; mais il ne fait pas exagérer non plus les risques, la technique est tellement commune qu'il va avoir de la pression des utilisateurs pour être supportée.
Foe,
Je ne vois pas quel pourrait être le problème dû à une optimisation : si on met des données dans une union, qui est une variable comme une autre, et qu'elles ne sont plus valides lorqu'on veut s'en servir, il faudra sérieusement penser à changer de compilateur.
Si les concepteurs de compilateurs en arrivent à faire de telles "optimisations", qui compromettent le fonctionnement d'un programme, il faudra également penser à leur expliquer qu'optimiser, c'est bien, mais qu'il faut réfléchir un peu avant de se permettre certaines choses.
Si tu mets ta donnée dans un membre de l'union et que te relis ce même membre tout ira bien (une variable comme une autre).
Si tu te sers d'une union pour éviter d'écrire reinterpret_cast, c'est à dire si tu mets ta donnée dans un membre, et que lis un autre membre (en l'occurrence, d'un autre type), tu n'utilises pas une union pour ce pour quoi elle est faite, et, selon la structure précise de ton code et le compilateur, tu pourrais ne pas obtenir ce que veux.
Quand gcc a commencé a simplifier agressivement les expressions arithmétiques, pour simplifier certains tests :
Quand gcc a décidé que a + 1000 n'était pas strictement inférieur à a, par définition, et que donc la fonction foo ne faisait jamais rien, certaines personnes ont essayé d'expliquer qu'"optimiser c'était bien", mais que là ça allait trop loin, tout ça.Code:
1
2
3
4 void foo (int a) { if (a + 1000 < a) overflow (); }
Ce à quoi il leur a été répondu qu'ils n'avaient qu'à apprendre à programmer correctement. (Avec quand même une distinction faite entre le vieux code hérité de quelqu'un d'autre, et le fait d'écrire de telles choses.)
Au final, gcc optimise de telles expressions, entre autres choses.
Et les protestations du genre "il faut réfléchir un peu avant de se permettre certaines choses" ne risquent pas d'impressionner beaucoup les développeurs du compilateur. Si tu veux défendre ton point de vue, tu as intérêt à développer des arguments autrement mieux étayés.
On a vu des compilateurs qui ne compilaient correctement que les tests de SPEC et faire des choses bizarres simplement pour ameliorer leur perfs a ce benchmark. Quand les performances sont un argument de vente -- et ce l'est pour les compilateurs comme pour les processeurs, ce n'est pas un hasard que les fabriquants des derniers soient impliques dans le developpement des premiers; Intel, IBM et Sun pour n'en citer que trois sponsorisent pas mal le developpement de gcc alors qu'ils ont leurs propres compilateurs -- il ne faut pas s'etonner que les gens qui travaillent sur l'optimisation utilisent tous les degres de liberte dont ils disposent.