3
votes

Ce type d'alias utilisant l'union appelle-t-il un comportement non défini?

Par exemple,

#include <cstdint>
#include <cstdio>

struct ipv4addr {
  union {
    std::uint32_t value;
    std::uint8_t parts[4];
  };
};

int main() {
  ipv4addr addr;
  addr.value = static_cast<std::uint32_t>(-1);
  std::printf("%hhu.%hhu.%hhu.%hhu",
    addr.parts[0], addr.parts[1], addr.parts[2], addr.parts[3]);
}  

Par cppref ,

Les détails de cette allocation sont définis par l'implémentation, et c'est comportement indéfini à lire du membre du syndicat qui n'était pas le plus récemment écrit.

On dirait donc que le code invoque un comportement non défini. Mais la page dit aussi

Si deux membres du syndicat sont des types de mise en page standard, il est bien défini pour examinez leur sous-séquence commune sur n'importe quel compilateur.

Je ne comprends pas très bien cela. Le comportement du code est-il bien défini?

Notez également la description de cppref sur alias de type .

Chaque fois qu'une tentative est faite pour lire ou modifier la valeur stockée d'un objet de type DynamicType via une valeur de glissement de type AliasType , le le comportement n'est pas défini, sauf si l'une des conditions suivantes est vraie:

  • [...]
  • AliasType est std :: byte , char , ou unsigned char : cela permet l'examen de l'objet représentation de tout objet sous la forme d'un tableau de octets.

Je suppose que cela s'applique également à std :: uint8_t . Non?


5 commentaires

cette question répond-elle à la vôtre?


Ceci < / a> peut être informatif.


Copie possible de Accès à un membre du syndicat inactif et à un comportement non défini?


J'avoue ne pas avoir la moindre idée de ce qui arrive à un uint8 sur un système où un octet fait 16 bits. Temps pour une plongée standard.


Ah ha! C'est facultatif! En d'autres termes, l'octet n'est pas 8 bits, cela ne se compilera probablement pas.


3 Réponses :


-2
votes

C'est bien défini pour faire exactement ce que fwrite suivi de quatre appels pour le relire. Le résultat réel dépend de la machine. "Obtenez-moi les octets pour cet uint32" quel que soit le mode de stockage que le CPU préfère.


0 commentaires

1
votes

La lecture du contenu d'un membre du syndicat qui n'a pas été affecté le plus récemment est en effet un comportement indéfini. Cependant, la plupart des compilateurs que j'ai utilisés ont des extensions non standard pour l'autoriser.

Techniquement, si vous voulez que cela soit sûr sans risque d'échec, vous devez stocker l'adresse IP sous int32_t et la réinterpréter via reinterpret_cast pour lire les octets individuels, comme ceci:

int32_t ip = 185734;
int8_t *ip_bytes = reinterpret_cast<int8_t*>(&ip).

ip_bytes[etc]...

Cependant, vous devez garder à l'esprit que l'endianité de votre plate-forme aura un impact sur l'ordre des octets pour toute lecture / écriture 32 bits. Par conséquent, il peut être plus sûr de supprimer complètement l'idée int32_t et d'utiliser simplement un tableau d'octets. Tout dépend de ce dont vous avez besoin et / ou de toute autre bibliothèque que vous utilisez.


2 commentaires

Je suppose que je vais m'en tenir à std :: uint32_t , mais obtenir parties par 256 modulo ( / 256 et % 256 consécutivement). Cela supprime également la dépendance à l'endianness.


@Lingxi Soit cela, soit au niveau du bit, selon ce qui est le plus clair. Le compilateur optimisera les deux de la même manière malgré tout. Et je recommanderais de créer un type de wrapper pour cela juste pour la lisibilité / la fiabilité.



3
votes

La sous-séquence initiale commune a une définition ridiculement spécifique. int et struct foo {int x;} n'ont pas de sous-séquence initiale commune.

struct foo {int x;} et struct bar {int y;} ont une sous-séquence initiale commune.

La lecture de mémoire via un type non lié n'est pas la même chose que la lecture à partir d'une alternative d'union. Ce texte ne fait rien ici.

Vous pouvez faire (std :: unit8_t const *) & addr.value et le traiter comme un tableau de 4 octets, en supposant que votre plate-forme a unit8_t . Les valeurs d'octet que vous obtenez sont définies par l'implémentation.

Vous ne pouvez pas, selon la norme, lire à partir de parties [i] (lorsque la valeur existe).

Les compilateurs sont libres de spécifier le comportement lorsque le standard déclare qu'il n'est pas défini sous le standard, sauf pendant une évaluation de constexpr lors de la compilation.


1 commentaires

Les membres d'un syndicat sont interconvertibles avec des pointeurs, n'obtenez-vous pas la même valeur de la distribution par rapport à l'accès direct?