Le problème est l'ordre d'évaluation:
La norme C++ ne définit pas l'ordre d'évaluation des sous-expressions. Ceci est fait pour que le compilateur puisse être aussi agressif que possible dans les optimisations.
Lets décomposer:
a1 a2
v = ((p[ i++ ] & 0xFF) << 4 | (p[ i ] & 0xF0000000) >> 28;
-----
(1) a1 = p[i]
(2) i = i + 1 (i++) after (1)
(3) a2 = p[i]
(4) t3 = a1 & 0xFF after (1)
(5) t4 = a2 & 0xF0000000 after (3)
(6) t5 = t3 << 4 after (4)
(7) t6 = t4 >> 28 after (5)
(8) t7 = t5 | t6 after (6) and (7)
(9) v = t7 after (8)
Maintenant, le compilateur est libre de réarranger expressions ainsi sous tant que ci-dessus « après » clauses ne sont pas violés.Donc, une optimisation facile rapide est déplacer 3 vers le haut d'un emplacement et ensuite supprimer l'expression commune (1) et (3) (maintenant à côté de l'autre) sont les mêmes et ainsi nous pouvons éliminer (3)
Mais le compilateur ne le fait pas avoir à faire l'optimisation (et est probablement meilleur que moi et a d'autres trucs dans sa manche). Mais vous pouvez voir comment la valeur de (a1) sera toujours ce que vous attendez, mais la valeur de (a2) dépendra de l'ordre dans lequel le compilateur décide de faire les autres sous-expressions. La seule garantie que vous avez que le compilateur ne peut pas déplacer les sous-expressions au-delà d'un point de séquence. Votre point de séquence le plus courant est ';' (la fin de la déclaration). Il y en a d'autres, mais j'éviterais d'utiliser cette connaissance car la plupart des gens ne connaissent pas bien le fonctionnement du compilateur. Si vous écrivez du code qui utilise des astuces de point de séquence, quelqu'un peut le factoriser de nouveau pour le rendre plus lisible et maintenant votre astuce vient de se transformer en un be-comportement indéfini.
short v = (p[ i++ ] & 0xFF) << 4;
v |= (p[ i ] & 0xF0000000) >> 28;
-----
(1) a1 = p[i]
(2) i = i + 1 (i++) after (1)
(4) t3 = a1 & 0xFF after (1)
(6) t5 = t3 << 4 after (4)
(A) v = t5 after (6)
------ Sequence Point
(3) a2 = p[i]
(5) t4 = a2 & 0xF0000000 after (3)
(7) t6 = t4 >> 28 after (5)
(8) t7 = v | t6 after (7)
(9) v = t7 after (8)
Ici, tout est bien défini lorsque l'écriture sur i est appliquée en place et n'est pas relue dans la même expression.
Règle simple. n'utilisez pas les opérateurs ++ ou - dans une expression plus grande. Votre code est tout aussi lisible comme ceci:
++i; // prefer pre-increment (it makes no difference here, but is a useful habit)
v = ((p[ i ] & 0xFF) << 4 | (p[ i ] & 0xF0000000) >> 28;
Voir cet article pour une explication détaillée de l'ordre d'évaluation:
What are all the common undefined behaviours that a C++ programmer should know about?
+1: Très bonne explication! Mais ta dernière suggestion est fausse! Il doit être: v = ((p [i] & 0xFF) << 4 | (p [i + 1] & 0xF0000000) >> 28; ++ i; – mmmmmmmm
@rstevens Ma dernière expression dépend de ce que 'Bjoern' essayait de faire, puisque son code actuel a un comportement indéfini qui décide de ce que le code 'doit être' dépendra de la façon dont 'Bjoern' interprétera ce que ++ faisait, et donc sans autre contexte il y a plusieurs veriants différents Je vois mon interprétation comme l'un de ces veriants et je suis sûr que 'Bjoern' peut extrapoler ce qu'il veut faire à partir de là –
PS Je suis d'accord que le ++ je pourrais potentiellement aller après l'expression, mais je ne suis pas d'accord le deuxième accès à p a besoin de +1 p [i +1] comme dans votre expression 'doit être', votre réponse 'doit donc être fausse' ;-) Voir les absolus sont rarement corrects. –