12
votes

Où est-ce exactement la norme C ++ dise la déséroférance un pointeur non défini est un comportement indéfini?

Jusqu'à présent, je ne peux pas trouver comment déduire que ce qui suit: xxx

est un comportement indéfini.

Tout d'abord, il y a 5,3.1 / 1 que déclare que * signifie indirecte qui convertit t * sur t . Mais cela ne dit rien à propos de UB.

Il y a souvent cité 3.7.3.2/4 Dire que l'utilisation de la fonction de distribution sur un pointeur non nulle rend le pointeur invalide et l'utilisation ultérieure ultérieure du pointeur non valide est UB. . Mais dans le code ci-dessus, il n'y a rien à propos de la classique.

Comment peut-on déduire de l'UB dans le code ci-dessus?


4 commentaires

Je suppose que cela vient de C standard 6.5.3.2/4


Que dit la norme sur l'initialisation et la déclaration des pointeurs? À votre connaissance, la déclaration n'initialise pas le pointeur, de sorte que cela pourrait être quelque chose, attribuant une valeur à l'endroit où les points pourraient faire n'importe quoi. Je peux me tromper;-)


N'est-ce pas un comportement non défini à lire de toute variable, pointeur ou non? Considérez que vous écrivez sur l'adresse pointue, mais vous lisez de le pointeur du processus.


J'ai récemment posé une question similaire et j'ai eu une assez bonne réponse: Stackoverflow.com/questions/43533262/...


7 Réponses :


3
votes

Je ne vais pas prétendre que je connais beaucoup de choses à ce sujet, mais certains compilateurs initialiseraient le pointeur à NULL et à la désérofacturation d'un pointeur à NULL est UB.

Considérant également que le pointeur ininitialisé pourrait indiquer quoi que ce soit (cela inclut NULL), vous pourriez conclure que c'est UB lorsque vous le déréférez.

une note de la section 8.3.2 [dcl.ref]

[NOTE: En particulier, une référence nulle ne peut pas exister dans un bien défini programme, parce que le seul moyen de créer une telle référence serait de Lier à l'objet obtenu par Déroferience d'un pointeur NULL, qui provoque un comportement non défini . Comme décrit en 9.6, une référence ne peut pas être lié directement sur un bitfield. ]

-iso / IEC 14882: 1998 (E), Norme ISO C ++, à la section 8.3.2 [DCL.RF]

Je pense que j'aurais dû écrire cela comme commentaire à la place, je ne suis pas vraiment sûr.


6 commentaires

Les notes ne sont pas contraignantes - elles sont réservées uniquement à des informations stackoverflow.com/questions/4274763/...


@shachartooth: Ils ne sont pas liant s'ils contredisent le texte normatif, mais à moins d'un défaut de la norme, tout ce qu'ils disent sur la langue est vrai . Je ne sais nulle part dans la norme qui dit que vous pouvez déréféralement un pointeur NULL :-) Les notes doivent être redondantes avec d'autres informations présentes ailleurs dans la norme. Mais la preuve qu'ils sont vraies peut nécessiter beaucoup de retourner à travers des sections, une déduction, etc. Un programmeur peut donc choisir de compter sur leur vérité, alors qu'un agent de mise en œuvre peut avoir besoin de savoir exactement pourquoi ils sont vrais et pourraient donc avoir besoin de la Texte normatif.


La déréférance d'un pointeur inintitualisé est définie. Il pointe de l'adresse de la mémoire (celle-ci n'est pas définie dans le programme) contenue dans la variable du pointeur. Dérofier à un pointeur NULL (où NULL est défini comme 0, non nullptr ou __nullptr) pointera vers l'adresse de la mémoire 0. Il existe des plates-formes où il est nécessaire d'accéder à l'adresse de la mémoire 0. Certains dispositifs incorporés par exemple.


@ T33C, je pense que vous pourriez être juste là. Je pense que vous ne pouvez pas obtenir au hasard le même "null" la note parle. (Je ne sais pas si ce que j'ai dit a du sens). Il existe toujours une question de la valeur aléatoire étant en dehors de la plage d'adresses de mémoire.


La note fait référence à la référence null. Nulle part dans la norme ne dit-il que la déséroférience null provoque un comportement non défini. Ce n'est que dans cette note qui dit que l'obtention d'une référence par la déséroférance d'un pointeur NULL est indéfinie mais elle utilise (ou crée) une telle référence non définie. Dérofier à un pointeur avec la valeur 0 (NULL) est un comportement défini et parfois nécessaire pour accéder à l'emplacement de la mémoire 0x0000 sur le matériel. J'ai besoin de le faire sur une photo (périphérique intégré)


@ T33C, la note première mentionne une référence NULL, puis à titre d'explication note que la déséroférance d'un pointeur NULL est indéfinie. Mais nous ne devrions pas être suivis de la part, car la question concerne un pointeur ininitialisé.



13
votes

Section 4.1 ressemble à un candidat ( met l'accent sur la mine ):

Un lvalue (3.10) d'un Type non fonctionnelle, sans tableau T peut être converti en une rvalue. Si t est un type incomplet, un programme qui nécessite cette conversion est mal formé. Si l'objet auquel le Lvalue fait référence n'est pas un objet de type T et n'est pas un objet d'un type dérivé de t, ou si l'objet est non initialisé , un programme qui nécessite cette conversion a comportement non défini . Si T est un Type de classe non-classe, le type de la rvalue est la version cv-peu qualifiée de T. Sinon, le type de la rvalue est T.

Je suis sûr que je viens de chercher "uninitial" dans la spécification peut vous trouver plus de candidats.


6 commentaires

J'ai appris de Johannes / Litb l'autre jour qu'il y a un peu de défaut dans la spécification ici: Open-Std.org/jtc1/sc22/wg21/docs/cwg_active.html#240 . Vous devez donc lire ce paragraphe avec une certaine sympathie au fait que, lorsqu'il est dit "non initialisé", la norme devrait vraiment être plus claire sur les valeurs non désinitialisées / indéterminées. Dans ce cas, l'objet est certainement ininitialisé, cependant, votre citation couvre donc.


Un document de normes mal formulées ?! Mais c'est désossé! ;)


@Stevejessop Je ne vois pas comment il s'applique dans ce cas, comment peut-il y avoir une conversion lvalue à rvalue lorsque vous avez * pTR = 0; ? Le résultat de * est un lvalue et = nécessite que la gauche ait eu un opérande à être un lvalue modifiable .


@Shafikyaghmour: PTR est un lvalue. Il y a une conversion LValue à rvalue appliquée à PTR afin d'évaluer * pTR .


@Stevejessop hmmm, pTR est un identifiant et le résultat de cette expression est lvalue s'il s'agit d'une variable, donc je ne vois toujours pas Il, par 5.1.1 p 8 .


@Shafikyaghmour: Dans * PTR Le * Opérateur unitaire a un seul opérande, PTR , qui est une expression de lvalue qui nécessite une conversion. Ce n'est pas différent de si vous écrivez a + b . Il existe une conversion lvalue-to-rvalue sur chaque opérande de + ( A et b ).



3
votes

à la désarférence Le pointeur, vous devez lire à partir de la variable du pointeur (ne pas parler de l'objet qu'il pointe de). La lecture d'une variable ininitialisée est un comportement indéfini.

Qu'est-ce que vous faites avec la valeur du pointeur après que vous l'avez lu, cela n'a plus d'importance à ce stade, soyez-le d'écrire à (comme dans votre exemple) ou de lire de l'objet qu'il pointe.


0 commentaires

5
votes

La question de l'OP est un non-sens. Il n'est pas nécessaire que la norme indique que certains comportements ne soient indéfinis et, en fait, je dirais que tout tel libellé soit supprimé de la norme car il confond des personnes et rend la standard plus verbeuse que nécessaire.

La norme définit certains comportements. La question est que cela spécifie-t-il un comportement dans ce cas? Si ce n'est pas le cas, le comportement est indéfini s'il le dit ou non explicitement.

En fait, les spécifications que certaines choses sont indéfinies sont laissées dans la norme principalement comme une aide de débogage pour les écrivains de normalisation, l'idée étant de générer une contradiction s'il existe une exigence dans un endroit qui est conflictuelle avec une déclaration explicite de comportement dans un autre: c'est un moyen de prouver un défaut de la norme. Sans l'énoncé explicite du comportement non défini, l'autre clause prescrivant le comportement serait normatif et incontesté.


1 commentaires

La norme définit certains comportements, mais devrait définir, et il a , dans une certaine mesure et la plupart du temps, défini certains comportements comme indéfinis - surtout lorsque de tels comportements sont dans les zones morales grises. La question est valide.



6
votes

J'ai trouvé la réponse à cette question est un coin inattendu du C ++ Standard Standard , section 24.2 Configuration itératrice , spécifiquement section 24.2.1 en général paragraphe em> 5 et 10 qui disent respectivement ( emphasis mine ):

[...] [[...] [exemple: après la déclaration d'un pointeur non initialisé x (comme avec int * x;), x doit toujours être supposé avoir une valeur singulière d'un pointeur . -End Exemple] [...] Les valeurs déséroférivables sont toujours non singulières.

et:

Un itérateur non valide est un itérateur qui peut être singulier. 268

et note de bas de page 268 dit:

Cette définition s'applique aux pointeurs, car les pointeurs sont des itérateurs . L'effet de la déséroférance d'un itérateur qui a été invalidé n'est indéfini.

Bien qu'il ressemble à une certaine controverse sur Un null Le pointeur est singulier ou non et on dirait que le terme la valeur singulière doit être correctement défini de manière plus générale.

L'intention de singulier semble être résumée bien dans le rapport de défaute 278. Que signifie la validité d'itérator? sous la section de justification qui dit:

Pourquoi disons-nous "peut être singulier" , au lieu de "singulier"? C'est parce que c'est une itératrice valide est connue pour être non confortable . Invalidation d'un itérateur signifie la modifier de manière à ce qu'il ne soit plus connu pour être non confortable. Un exemple: l'insertion d'un élément au milieu d'un vecteur est correctement dit d'invalider tous les itérateurs pointant dans le vecteur. Que ne signifie pas nécessairement qu'ils deviennent tous singuliers .

SO Invalidation et étant nonIuitialisé peut créer une valeur singulière mais puisque nous ne pouvons pas prouver qu'ils ne peuvent pas prouver qu'ils ne peuvent pas prouver qu'ils sont non singular nous devons supposer qu'ils sont singulier .

mise à jour

Une approche de sens commune alternative serait de noter que le projet de section standard 5.3.1 les opérateurs unaires paragraphe 1 qui dit ( L'accent est mis sur le mien ):

L'opérateur unaire * effectue une indirection: l'expression auquel elle est appliquée doit être un pointeur sur un type d'objet, ou un pointeur sur un type de fonction et le résultat est un lvalue faisant référence à l'objet ou fonction à laquelle les points d'expression. [...]

et si nous allons ensuite à la section 3.10 lvalues ​​et rvalues ​​ paragraphe 1 dit ( emphasis mine ):

Un lvalue (ainsi appelé, historiquement, car les lvalues ​​pouvaient apparaître sur le côté gauche d'une expression d'affectation) désignent une fonction ou un objet. [...]

mais pTR ne sera pas, sauf par hasard, pointez sur un objet valide . .


0 commentaires

3
votes

évaluer un pointeur ininitialisé provoque un comportement non défini. Depuis la déséroférence, le pointeur nécessite d'abord l'évaluation, cela implique que la déséroférience provoque également un comportement non défini.

Ceci était vrai en C ++ 11 et C ++ 14, bien que le libellé a changé.

en C ++ 14 Il est entièrement recouvert de [dcl.init] / 12:

Lorsque le stockage d'un objet avec une durée de stockage automatique ou dynamique est obtenu, l'objet a une valeur indéterminée, et si aucune initialisation n'est effectuée pour l'objet, cet objet conserve une valeur indéterminée jusqu'à ce que cette valeur soit remplacée.

Si une valeur indéterminée est produite par une évaluation, le comportement est indéfini, sauf dans les cas suivants:

où les "cas suivants" sont des opérations particulières sur non signé char .


en C ++ 11, [CONV.LVAL / 2] couvrait ceci sous la procédure de conversion de LValue-à-rvalue (c'est-à-dire la récupération de la valeur du pointeur de la zone de stockage indiquée par PTR ): < / p>

Un glvalue d'une non-fonction, type Type Type T peut être converti en prjectif. Si T est un type incomplet, un programme qui nécessite cette conversion est mal formé. Si l'objet à laquelle la glvalue fait référence n'est pas Un objet de type T et n'est pas un objet d'un type dérivé de T, ou si l'objet est ininitialisé, un programme qui nécessite cette conversion a un comportement indéfini.

La partie en gras a été supprimée pour C ++ 14 et remplacée par le texte supplémentaire dans [DCL.Init / 12].


0 commentaires

1
votes

Même si le stockage normal de quelque chose en mémoire n'aurait aucune "pièce" pour tout bits d'interruption ou représentations de piège, des implémentations ne sont pas nécessaires pour stocker des variables automatiques de la même manière que les variables de durée statique, sauf lorsqu'il y a une possibilité que l'utilisateur Le code pourrait détenir un pointeur quelque part. Ce comportement est le plus visible avec des types entiers. Sur un système typique 32 bits, étant donné le code: xxx

Il ne serait pas particulièrement surprenant pour le test pour donner 65540 Même si cette valeur est en dehors de la plage représentable de uint16_t , un type qui n'a pas de représentation piège. Si une variable locale de type uint16_t contient la valeur indéterminée, il n'est pas nécessaire de la lire donner une valeur dans la plage de uint16_t . Puisque les comportements inattendus pouvaient résulter lors de l'utilisation même des entiers non signés de cette manière, il n'y a aucune raison de s'attendre à ce que les pointeurs ne puissent pas se comporter de manière encore pire.


0 commentaires