Stocker un pointeur non valide est-il automatiquement un comportement non défini?

Évidemment, la déréférence d’un pointeur non valide provoque un comportement indéfini. Mais qu’en est-il simplement de stocker une adresse mémoire invalide dans une variable de pointeur?

Considérons le code suivant:

const char* str = "abcdef"; const char* begin = str; if (begin - 1 < str) { /* ... do something ... */ } 

L’expression begin - 1 évaluée à une adresse mémoire non valide. Notez que nous ne déréférencons pas cette adresse – nous l’utilisons simplement dans l’arithmétique de pointeur pour vérifier si elle est valide. Néanmoins, nous devons toujours charger une adresse mémoire invalide dans un registre.

Alors, s’agit-il d’un comportement indéfini? Je n’avais jamais pensé que c’était vrai, car beaucoup d’arithmétique de pointeur semble s’appuyer sur ce genre de chose, et un pointeur n’est en réalité qu’un entier. Mais récemment, j’ai entendu dire que même le fait de charger un pointeur non valide dans un registre constituait un comportement indéfini, car certaines architectures génèreraient automatiquement une erreur de bus ou quelque chose du genre. Quelqu’un peut-il m’indiquer la partie pertinente de la norme C ou C ++ qui règle cela d’une manière ou d’une autre?

J’ai le projet de norme C ici, et cela le rend indéfini par omission. Il définit le cas de ptr + I à 6.5.6 / 8 pour

  • Si l’opérande de pointeur pointe sur un élément d’un object tableau et que le tableau est suffisamment grand, le résultat pointe sur un élément décalé par rapport à l’élément d’origine, de sorte que la différence entre les indices des éléments de tableau résultant et original est égale à l’expression entière.
  • De plus, si l’expression P pointe vers le dernier élément d’un object tableau, l’expression (P) +1 pointe un après le dernier élément de l’object tableau et si l’expression Q pointe un après le dernier élément d’un object tableau, l’expression (Q) -1 pointe vers le dernier élément de l’object tableau.

Votre cas ne correspond à aucun d’entre eux. Votre tableau n’est pas non plus assez grand pour que -1 ajuste le pointeur de manière à pointer vers un élément de tableau différent, pas plus que le résultat ou le pointeur d’origine ne pointe d’un côté.

Votre code est un comportement indéfini pour une raison différente:

l’expression begin - 1 ne donne pas un pointeur invalide. C’est un comportement indéfini. Vous n’êtes pas autorisé à effectuer des opérations arithmétiques au-delà des limites du tableau sur lequel vous travaillez. C’est donc la soustraction elle-même qui est invalide et non l’acte de stocker le pointeur résultant.

Certaines architectures ont des registres dédiés pour contenir des pointeurs. Mettre la valeur d’une adresse non mappée dans un tel registre est autorisé à planter. Les débordements / dépassements entiers sont autorisés à se bloquer. C souhaitant travailler sur une grande variété de plates-formes, les pointeurs constituent un mécanisme permettant de programmer en toute sécurité des circuits non sécurisés.

Si vous savez que vous n’utiliserez pas de matériel exotique doté de caractéristiques aussi pointilleuses, vous n’avez pas à vous soucier de ce qui n’est pas défini par la langue. Il est bien défini par la plateforme.

Bien sûr, l’exemple est de style médiocre et il n’y a pas de bonne raison de le faire.

Toute utilisation d’un pointeur invalide entraîne un comportement indéfini. Je n’ai pas la norme C ici au travail, mais je vois des «pointeurs invalides» dans la justification: http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf

$ 5.7 / 6 – “À moins que les deux pointeurs pointent vers des éléments du même object de tableau, ou un élément après le dernier élément de l’object de tableau, le comportement est indéfini.75)”

En résumé, il n’est pas défini même si vous ne déréférenciez pas le pointeur .

Les bonnes réponses ont été données il y a des années, mais je trouve intéressant que la justification du C99 [sec. 6.5.6, les 3 derniers paragraphes] explique pourquoi la norme recommande d’append 1 à un pointeur qui pointe vers le dernier élément d’un tableau ( p+1 ):

Une recommandation importante de la pratique répandue est l’exigence selon laquelle un pointeur peut toujours être incrémenté juste au-delà de la fin d’un tableau, sans crainte de débordement ou d’enveloppement.

et pourquoi p-1 n’est pas approuvé:

Dans le cas de p-1, par contre, un object entier devrait être alloué avant le tableau d’objects que p parcourt. Par conséquent, les boucles de décrémentation exécutées au bas d’un tableau peuvent échouer. Cette ressortingction permet aux architectures segmentées, par exemple, de placer des objects au début d’une plage de mémoire adressable.

Donc, si le pointeur p pointe sur un object au début d’une plage de mémoire adressable, qui est endossée par ce commentaire, alors p-1 générera un dépassement de capacité.

Notez que le dépassement d’entier est l’exemple de la norme pour le comportement indéfini [sec. 3.4.3], car cela dépend de l’environnement de traduction et de l’environnement d’exploitation. Je crois qu’il est facile de voir que cette dépendance à l’environnement s’étend au débordement du pointeur.

C’est pourquoi la norme rend explicitement ce comportement indéfini [au 6.5.6 / 8], comme indiqué par d’autres réponses ici. Pour citer cette phrase:

Si l’opérande de pointeur et le résultat pointent tous deux sur des éléments du même object de tableau, ou d’un élément passé le dernier élément de l’object de tableau, l’évaluation ne doit pas produire de dépassement de capacité; sinon, le comportement n’est pas défini.

Voir aussi [sec. 6.3.2.3, les 4 derniers paragraphes] de la justification du C99, qui donne une description plus détaillée de la manière dont des pointeurs non valides peuvent être générés et des effets que cela peut avoir.

Oui, c’est un comportement indéfini. Voir la réponse acceptée à cette question étroitement liée . L’affectation d’un pointeur non valide à une variable, la comparaison d’un pointeur non valide, le transtypage d’un pointeur non valide déclenchent un comportement non défini.