38 votes

Est-il bien défini de tenir un pointeur mal aligné, tant que vous n'avez #39;t jamais le déréférencer?

J'ai du code C qui traite les paniers/unpadded données binaires qui vient par le réseau.

Ce code a été/est fonctionne bien sous Intel/x86, mais quand j'ai compilé sous le BRAS, il serait souvent se bloquer.

Le coupable, comme vous l'avez deviné, était non alignés pointeurs-en particulier, l'analyse de code douteux des choses comme ceci:

uint8_t buf[2048];
[... code to read some data into buf...]
int32_t nextWord = *((int32_t *) &buf[5]);  // misaligned access -- can crash under ARM!

... c'est de toute évidence ne va pas à voler dans les BRAS de la terre, alors je l'ai modifié pour ressembler à ceci:

uint8_t buf[2048];
[... code to read some data into buf...]
int32_t * pNextWord = (int32_t *) &buf[5];
int32 nextWord;
memcpy(&nextWord, pNextWord, sizeof(nextWord));  // slower but ARM-safe

Ma question (à partir d'une langue-avocat point de vue), c'est que mon "BRAS-fixe" approche bien définie en vertu de la C les règles de la langue?

Mon souci, c'est que peut-être même juste en ayant un mal alignées-int32_t-pointeur peut être suffisant pour invoquer un comportement indéfini, même si je n'ai jamais fait de déréférencement directement. (Si ma préoccupation est valide, je pense que je pourrais résoudre le problème en changeant pNextWord's type d' (const int32_t *) de (const char *), mais je préfère ne pas le faire sauf si c'est réellement nécessaire de le faire, puisqu'il faudrait faire quelques pointeur de la foulée de l'arithmétique à la main)

22voto

Antti Haapala Points 11542

Non, le nouveau code a un comportement indéterminé. C11 6.3.2.3p7:

  1. Un pointeur vers un type d'objet peut être converti en un pointeur vers un autre type d'objet. Si le pointeur n'est pas correctement aligné 68) pour le type référencé, le comportement est indéfini. [...]

Il ne dit rien sur le déréférencement du pointeur - même la conversion a un comportement indéterminé.


En effet, la modification de code que vous assumez est BRAS-safe peut-être pas encore Intel-fort. Les compilateurs sont connus pour générer du code pour Intel qui peuvent tomber en panne sur l'accès non alignés. Tout en n'étant pas liées cas, c'est peut-être qu'un habile compilateur peut prendre la conversion comme une preuve que l'adresse est en effet aligné et utiliser un code spécialisé pour memcpy.


L'alignement de côté, votre premier extrait souffre également de la stricte aliasing violation. C11 6.5p7:

  1. Un objet doit avoir sa valeur stockée et accessible uniquement par une lvalue expression qui est l'un des types suivants:88)
    • un type compatible avec l'efficacité du type de l'objet,
    • une version qualifiée d'un type compatible avec l'effectif type de l'objet,
    • un type qui est signé ou non signé de type correspondant à l'efficacité du type de l'objet,
    • un type qui est signé ou non signé de type correspondant à une version qualifiée de l'effectif type de l'objet,
    • une agrégation ou une union de type qui comprend l'un des types mentionnés ci-dessus, parmi ses membres, (y compris, de manière récursive, un membre d'un subaggregate ou contenus de l'union), ou
    • un type de caractère.

Depuis le tableau buf[2048] est statiquement typé, chaque élément est - char, et donc l'efficacité de types d'éléments sont char; vous pouvez accéder au contenu du tableau seulement , les personnages, non pas comme int32_ts.

I. e., même

int32_t nextWord = *((int32_t *) &buf[_Alignof(int32_t)]);

a un comportement indéterminé.

8voto

lee qiaoping Points 155

Pour analyser en toute sécurité l'entierceur multi-octets sur les compilateurs/plates-formes, vous pouvez extraire chaque octet, et les assembler à l'intégrine selon l'endian. Par exemple, pour lire 4-octet integer de tampon big-endian:

4voto

supercat Points 25534

Certains compilateurs peuvent assumer aucune pointeur sera jamais une valeur qui n'est pas correctement aligné pour son type, et de procéder à des optimisations qui font que. Comme exemple simple, imaginons:

void copy_uint32(uint32_t *dest, uint32_t *src)
{
  memcpy(dest, src, sizeof (uint32_t));
}

Si les deux dest et src sont de 32 bits alignés adresses, la fonction ci-dessus pourrait être optimisé pour un chargement et d'un magasin, même dans les plates-formes qui ne prennent pas en charge les accès non alignés. Si la fonction avait été déclaré accepter les arguments de type void*, cependant, cette optimisation ne serait pas autorisé sur les plates-formes où les non alignés 32 bits accède allait se comporter différemment à partir d'une séquence d'octets accède à, quarts de travail, et bit à bit des opérations.

2voto

dbush Points 8590

Comme mentionné dans Antti de la Haapala de répondre, il suffit de la conversion d'un pointeur vers un autre type lorsque le pointeur résultant n'est pas correctement aligné invoque un comportement non défini conformément à la section 6.3.2.3p7 du C standard.

Votre code modifié seules les utilisations pNextWord pour passer à l' memcpy, où il est converti en void *, de sorte que vous n'avez même pas besoin d'une variable de type uint32_t *. Il suffit de passer l'adresse du premier octet dans la mémoire tampon que vous souhaitez lire à partir d' memcpy. Ensuite, vous n'avez pas besoin de vous soucier de l'alignement.

uint8_t buf[2048];
[... code to read some data into buf...]
int32_t nextWord;
memcpy(&nextWord, &buf[5], sizeof(nextWord));

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