C – Accéder à une déclaration non-const via const

L’access à un object non const via une déclaration const autorisé par le standard C? Par exemple, le code suivant est-il garanti pour la compilation et la sortie 23 et 42 sur une plate-forme conforme aux normes?

unité de traduction A:

 int a = 23; void foo(void) { a = 42; } 

unité de traduction B:

 #include  extern volatile const int a; void foo(void); int main(void) { printf("%i\n", a); foo(); printf("%i\n", a); return 0; } 

Dans l’ISO / CEI 9899: 1999, je viens de découvrir (6.7.3, paragraphe 5):

Si vous tentez de modifier un object défini avec un type qualifié de const en utilisant une valeur lvalue avec un type non qualifié de const, le comportement est indéfini.

Mais dans le cas ci-dessus, l’object n’est pas défini en tant que const (mais simplement déclaré).

METTRE À JOUR

Je l’ai finalement trouvé dans ISO / IEC 9899: 1999.

6.2.7, 2

Toutes les déclarations qui font référence au même object ou à la même fonction doivent avoir un type compatible; sinon, le comportement n’est pas défini.

6.7.3, 9

Pour que deux types qualifiés soient compatibles, les deux doivent avoir la version identique d’un type compatible; […]

Donc, c’est un comportement indéfini.

TU A contient la (seule) définition de a . Donc, a est vraiment un object non-const, et on peut y accéder en tant que tel à partir d’une fonction en A sans aucun problème.

Je suis à peu près sûr que TU B invoque un comportement indéfini, car sa déclaration de ne correspond pas à la définition. La meilleure citation que j’ai trouvée jusqu’à présent pour confirmer que c’est UB est 6.7.5 / 2:

Chaque déclarant déclare un identificateur et affirme que lorsqu’un opérande de la même forme que le déclarant apparaît dans une expression, il désigne une fonction ou un object avec la scope, la durée de stockage et le type indiqués par les spéci fi cateurs de déclaration.

[Edit: le questionneur a depuis trouvé la référence appropriée dans la norme, voir la question.]

Ici, la déclaration en B affirme que a est de type volatile const int . En fait, l’object n’a pas de type (qualifié) volatile const int , il a le type (qualifié) int . La violation de la sémantique est UB.

En pratique, ce qui va arriver, c’est que TU A sera compilé comme si a est non const. La TU B sera compilée comme si était un volatile const int , ce qui signifie qu’elle ne mettra pas en cache la valeur de a . Ainsi, je m’attendrais à ce que cela fonctionne tant que l’éditeur de liens ne remarque ni ne s’objecte aux types incompatibles, car je ne vois pas immédiatement comment TU B pourrait éventuellement émettre un code erroné. Cependant, mon manque d’imagination n’est pas la même chose qu’un comportement garanti.

Pour autant que je sache, rien dans la norme n’indique que volatile objects volatile au niveau du fichier ne peuvent pas être stockés dans une banque de mémoire complètement différente de celle des autres objects, qui fournit des instructions différentes pour les lire. L’implémentation doit toujours être capable de lire un object normal via, par exemple, un pointeur volatile , supposons par exemple que l’instruction de chargement “normale” fonctionne sur des objects “spéciaux” et qu’elle l’utilise lors de la lecture d’un pointeur vers un object. type qualifié volatile. Mais si (en tant qu’optimisation) l’implémentation a émis l’instruction spéciale pour les objects spéciaux, et que l’instruction spéciale ne fonctionne pas sur les objects normaux, alors boum. Et je pense que c’est la faute du programmeur, même si j’avoue que je n’ai inventé cette implémentation que il y a 2 minutes, je ne peux donc pas être tout à fait sûr qu’elle est conforme.

Dans l’unité de traduction B, const interdirait uniquement la modification de la variable a dans l’unité de traduction B elle-même.

Les modifications de cette valeur provenant de l’extérieur (autres unités de traduction) auront un impact sur la valeur que vous voyez en B.

C’est plus un problème d’éditeur de liens qu’un problème de langue. L’éditeur de liens est libre de faire abstraction des qualifications différentes du symbole (s’il existe de telles informations dans les fichiers d’object) lors de la fusion des unités de traduction compilées.

Notez cependant que si c’est l’inverse ( const int a = 23 dans A et extern int a dans B), vous risquez de rencontrer une violation d’access à la mémoire en cas de tentative de modification de a depuis B, car a pourrait être placé dans une zone du processus en lecture seule, généralement mappée directement à partir de la section .rodata de l’exécutable.

La déclaration qui a l’initialisation est la définition, votre object n’est donc pas un object qualifié de const et foo a tous les droits pour le modifier.

En B, vous fournissez un access à cet object qui possède la qualification supplémentaire const . Étant donné que les types (la version qualifiée const et la version non qualifiée) ont la même représentation d’object, l’access en lecture via cet identificateur est valide.

Votre deuxième printf , cependant, a un problème. Comme vous n’avez pas qualifié votre version B de volatile vous ne serez pas assuré de voir la modification de a . Le compilateur est autorisé à optimiser et à réutiliser la valeur précédente qu’il aurait pu conserver dans un registre.

Le déclarer en tant que const signifie que l’instance est définie en tant que const. Vous ne pouvez pas y accéder depuis un non-const. La plupart des compilateurs ne le permettront pas, et la norme dit que ce n’est pas permis non plus.

FWIW: Dans H & S5, il est écrit (Section 4.4.3 Qualificatifs de type, page 89): “Utilisés dans un contexte nécessitant une valeur plutôt qu’un indicateur, les qualificateurs sont éliminés du type.” Ainsi, la const n’a d’effet que lorsque quelqu’un essaie d’écrire quelque chose dans la variable. Dans ce cas, printf utilise une valeur rvalue, et le caractère volatile (à mon humble avis inutile) fait en sorte que le programme lise la variable de nouveau. Je dirais donc que le programme est requirejs pour produire le résultat que le PO a vu initialement, sur tous plates-formes / compilateurs. Je vais regarder la norme et l’append si / quand je trouve quelque chose de nouveau.

EDIT: Je n’ai pas trouvé de solution définitive à cette question dans la norme (j’ai utilisé le dernier projet pour C1X ), car toutes les références au comportement de l’éditeur de liens se concentrent sur les noms identiques. Les qualificatifs de type sur les déclarations externes ne semblent pas être couverts. Peut-être devrions-nous transmettre cette question au Comité de la norme C.