valeur - retour par reference c++




Conversion du tableau volatile en tableau non volatile (3)

J'ai un tableau global de caractères volatiles non signés volatils non volatile unsigned char buffer[10] dans lesquels les données sont écrites dans un intervalle. J'ai une fonction qui prend un caractère non signé * et stocke cette valeur dans le matériel (EEPROM) void storeArray(unsigned char *array) , dans cet exemple les trois premières valeurs. Est-il prudent de transtyper le tableau volatile en un tableau non volatile comme ceci?

storeArray((unsigned char *) buffer);

J'ai lu ce qui suit, que je ne comprends pas bien, mais qui me concerne:

6.7.3: 5 Si l'on tente de faire référence à un objet défini avec un type qualifié volatil en utilisant une valeur lvalue avec un type qualifié non volatile, le comportement est indéfini.

Est-ce que cela affecte mon code?

Ensuite, j'ai la question suivante: le tableau de mémoire tampon ne contient qu'une partie des données que je veux stocker (je ne peux pas changer cela), pour cet exemple commençant par la troisième valeur. Est-il légitime de faire ce qui suit?

storeArray((unsigned char *) buffer + 3);

Si c'est le cas, comment la distribution est-elle affectée, si 3 est ajouté au tableau? BR et merci!

EDIT: @Cacahuete Frito a lié une question très similaire: Est-ce que `memcpy ((void *)) dest, src, n)` est protégé par un tableau `volatile`?


  1. Si le tableau est modifié en interruption, vous devez fournir un mécanisme pour y accéder et le modifier de façon atomique. Sinon, aucune opération RW ou RMW peut échouer et les données devenir incohérentes.

  2. Vous accédez à des données volatiles rendant également les paramètres f = onction volatiles. storeArray(volatile unsigned char *) et aucun cast n'est nécessaire. Le casting ne supprime que l'avertissement. Même si vous lui transmettez des données non volatiles, cela fonctionnera également.


Comme vous l'avez constaté, vous vous appuyez sur un "comportement non défini". Cependant, en fonction, entre autres, de la séparation des unités de compilation (et d’options telles que "l’optimisation de tout le programme" (WPO)), cela fonctionnera probablement. Dans la plupart des cas, le compilateur (au moins gcc) n'est pas "assez intelligent" pour optimiser les accès au tableau à travers les fonctions de différentes unités de compilation. Cela dit, la méthode propre, sûre et portable consisterait à copier le tableau, rendant visible la dépendance des valeurs du tableau non volatile par rapport aux valeurs volatiles pour le compilateur.


Vous avez trouvé la bonne section de la norme, ce code conduit à un comportement indéfini.

Une fonction qui écrit quelque chose "au matériel" devrait probablement avoir un paramètre volatile -qualifier, selon le "matériel". S'il s'agit d'un registre mappé en mémoire, d'une mémoire tampon DMA ou d'une mémoire non volatile, le paramètre doit alors volatile uint8_t* être volatile unsigned char* (ou éventuellement volatile uint8_t* , qui doit également être considéré comme un type de caractère).

Détails: C nous permet de parcourir n'importe quel bloc de données en utilisant un pointeur de caractère, C17 6.3.2.3/7:

Lorsqu'un pointeur sur un objet est converti en un pointeur sur un type de caractère, le résultat pointe sur l'octet adressé le plus bas de l'objet. Les incréments successifs du résultat, jusqu'à la taille de l'objet, donnent des pointeurs sur les octets restants de l'objet.

La partie que vous citez sur l'accès à une "lvalue" fait référence à l'accès aux données via un type de pointeur différent de celui stocké à cet endroit. En clair: quel que soit le nombre de pointeurs que vous lancez, les données réelles conservent leur type d'origine.

L'accès aux données par le biais d'un type de pointeur incorrect n'est généralement même pas autorisé, mais l'accès aux caractères est une exception spéciale à la "règle de repliement strict", C17 6.5 / 7:

La valeur stockée d'un objet doit être accessible uniquement par une expression lvalue qui possède l'un des types suivants:
...
- un type de caractère.

Vous pouvez donc accéder à n'importe quel type de données via un pointeur de caractère, mais si ce pointeur n'est pas qualifié de manière volatile, vous invoquez un comportement indéfini conformément à la partie que vous avez citée, C17 6.7.3 / 5.

En pratique, l'utilisation d'un type de pointeur non volatile peut amener le compilateur à optimiser l'accès de manière inattendue. Il ne s’agit donc pas simplement d’une "théorie juridique du langage": vous pouvez obtenir un code très étrange généré avec des optimisations activées. Beaucoup de bugs très difficiles à trouver dans les systèmes embarqués proviennent d'un tel volatile manquant.

En ce qui concerne votre question suivante, la distribution et le buffer + 3 ne changent rien: vous avez toujours affaire à un pointeur de caractère sans qualificatif volatile - même type. Les données réelles restent du type volatile unsigned char , vous ne pouvez donc pas y accéder depuis la fonction via un caractère unsigned char* .







volatile