53 votes

Est-ce que std :: memcpy entre différents types de comportements trivialement copiables n'est pas défini?

J'ai été en utilisant std::memcpy de contourner stricte aliasing pendant une longue période.

Par exemple, l'inspection d'un float, comme ceci:

float f = ...;
uint32_t i;
static_assert(sizeof(f)==sizeof(i));
std::memcpy(&i, &f, sizeof(i));
// use i to extract f's sign, exponent & significand

Cependant, cette fois, j'ai vérifié la norme, je n'ai rien trouvé qui valide cette. Tout ce que je trouve est ceci:

Pour tout objet (autre que potentiellement-le chevauchement des sous-objet) de la trivialement copiable type T, si oui ou non l'objet titulaire d'un permis de valeur de type T, le sous-jacent octets ([intro.mémoire]) faisant l'objet peut être copié dans un tableau de char, unsigned char, ou std::byte ([cstddef.syn]).40 Si le contenu de ce tableau est copié en arrière dans l'objet, l'objet doit ensuite tenir sa valeur d'origine. [ Exemple:

#define N sizeof(T)
char buf[N];
T obj;                          // obj initialized to its original value
std::memcpy(buf, &obj, N);      // between these two calls to std​::​memcpy, obj might be modified
std::memcpy(&obj, buf, N);      // at this point, each subobject of obj of scalar type holds its original value

fin de l'exemple ]

et ce:

Pour toute trivialement copiable type T, si deux pointeurs vers T point distinct T des objets obj1 et obj2, où ni obj1 ni obj2 est potentiellement un chevauchement sous-objet, si le sous-jacent octets ([intro.mémoire]) qui obj1 sont copiés dans obj2,41 obj2 doit ensuite contenir la même valeur qu'obj1. [ Exemple:

T* t1p;
T* t2p;
// provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
// at this point, every subobject of trivially copyable type in *t1p contains
// the same value as the corresponding subobject in *t2p

fin de l'exemple ]

Donc, std::memcpying float /à partir de char[] est autorisé, et std::memcpying entre les mêmes trivial types est aussi permis.

C'est mon premier exemple (et le lien de réponse) bien définis? Ou de la bonne façon à examiner un float est std::memcpy en unsigned char[] de la mémoire tampon, et l'aide d' shifts et ors pour construire un uint32_t - il?


Remarque: en regardant std::memcpy's garanties ne peuvent pas répondre à cette question. Pour autant que je sais, je pourrais remplacer std::memcpy avec un simple octet-copie de la boucle, et la question sera la même.

19voto

user2079303 Points 4916

C'est mon premier exemple (et le lien de réponse) bien définis?

Le comportement n'est pas indéfini (à moins que le type de cible a piège des représentations qui ne sont pas partagés par le type de source), mais la valeur de l'entier est définie par l'implémentation. La norme ne donne aucune garantie à propos de la façon dont les nombres à virgule flottante sont représentés, il n'existe aucun moyen d'extraire de la mantisse, etc à partir de l'entier dans mobiles - cela dit, vous vous limitez à la norme IEEE 754 à l'aide de systèmes ne vous limite pas beaucoup ces jours-ci.

Les problèmes de portabilité:

  • La norme IEEE 754 est pas garanti par le C++
  • Octet boutisme de flotteur n'est pas garanti match entier boutisme.
  • (Systèmes avec piège des représentations).

Vous pouvez utiliser std::numeric_limits::is_iec559 pour vérifier si votre hypothèse au sujet de la représentation est correcte.

Bien, il semble que l' uint32_t ne peut pas avoir des pièges (voir commentaires) de sorte que vous n'avez pas besoin d'être concernés. En utilisant uint32_t, vous avez déjà écarté la portabilité ésotériques systèmes standard conforme systèmes ne sont pas besoin pour définir cet alias.

15voto

Gill Bates Points 3884

Votre exemple est bien définie et ne rompt pas stricte de l'aliasing. std::memcpy stipule clairement:

Des Copies count d'octets à partir de l'objet pointé par la src à l'objet pointé par dest. Les deux objets sont réinterprétés comme des tableaux de unsigned char.

La norme permet d'aliasing n'importe quel type par le biais d'un (signed/unsigned) char* ou std::byte et donc votre exemple ne présente pas les UB. Si l'entier résultant de la valeur du produit est une autre question si.


use i to extract f's sign, exponent & significand

Cependant, cela n'est pas garanti par la norme, la valeur de float est mise en œuvre définies (dans le cas de la norme IEEE 754, il va fonctionner, cependant).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X