Est-ce un comportement non défini pour imprimer des pointeurs nuls avec le %p
spécificateur de conversion?
#include <stdio.h>
int main(void) {
void *p = NULL;
printf("%p", p);
return 0;
}
La question s'applique au standard C, et non aux implémentations C.
c
language-lawyer
c99
undefined-behavior
c11
Dror K.
la source
la source
Réponses:
C'est l'un de ces cas étranges où nous sommes soumis aux limitations de la langue anglaise et à la structure incohérente de la norme. Donc, au mieux, je peux faire un contre-argument convaincant, car il est impossible de le prouver :) 1
Le code de la question présente un comportement bien défini.
Comme [7.1.4] est la base de la question, commençons par là:
C'est un langage maladroit. Une interprétation est que les éléments de la liste sont UB pour toutes les fonctions de la bibliothèque, à moins qu'ils ne soient remplacés par les descriptions individuelles. Mais la liste commence par "comme", indiquant que c'est illustratif et non exhaustif. Par exemple, il ne mentionne pas la terminaison nulle correcte des chaînes (critique pour le comportement de par exemple
strcpy
).Il est donc clair que l'intention / la portée de 7.1.4 est simplement qu'une "valeur invalide" mène à UB ( sauf indication contraire ). Nous devons examiner la description de chaque fonction pour déterminer ce qui compte comme "valeur invalide".
Exemple 1 -
strcpy
[7.21.2.3] dit seulement ceci:
Il ne fait aucune mention explicite des pointeurs nuls, mais il ne fait pas non plus mention des terminateurs nuls. Au lieu de cela, on déduit de "chaîne pointée par
s2
" que les seules valeurs valides sont des chaînes (c'est-à-dire des pointeurs vers des tableaux de caractères terminés par un nul).En effet, ce modèle peut être vu à travers les descriptions individuelles. Quelques autres exemples:
Exemple 2 -
printf
[7.19.6.1] dit ceci à propos de
%p
:Null est une valeur de pointeur valide, et cette section ne fait aucune mention explicite que null est un cas particulier, ni que le pointeur doit pointer sur un objet. C'est donc un comportement défini.
1. À moins qu'un auteur de normes ne se présente, ou à moins que nous puissions trouver quelque chose de similaire à un document de justification qui clarifie les choses.
la source
La réponse courte
Oui . L'impression de pointeurs nuls avec le
%p
spécificateur de conversion a un comportement non défini. Cela dit, je ne connais aucune implémentation conforme existante qui se conduirait mal.La réponse s'applique à n'importe laquelle des normes C (C89 / C99 / C11).
La longue réponse
Le
%p
spécificateur de conversion attend un argument de type pointeur vers void, la conversion du pointeur en caractères imprimables est définie par l'implémentation. Il n'indique pas qu'un pointeur nul est attendu.L'introduction aux fonctions de bibliothèque standard indique que les pointeurs nuls en tant qu'arguments aux fonctions (de bibliothèque standard) sont considérés comme des valeurs non valides, sauf indication contraire explicite.
C99
/C11
§7.1.4 p1
Exemples de fonctions (bibliothèque standard) qui attendent des pointeurs nuls comme arguments valides:
fflush()
utilise un pointeur nul pour vider "tous les flux" (qui s'appliquent).freopen()
utilise un pointeur nul pour indiquer le fichier "actuellement associé" au flux.snprintf()
permet de passer un pointeur nul lorsque 'n' vaut zéro.realloc()
utilise un pointeur nul pour allouer un nouvel objet.free()
permet de passer un pointeur nul.strtok()
utilise un pointeur nul pour les appels suivants.Si nous prenons le cas pour
snprintf()
, il est logique d'autoriser le passage d'un pointeur nul lorsque 'n' est égal à zéro, mais ce n'est pas le cas pour les autres fonctions (bibliothèque standard) qui autorisent un zéro similaire 'n'. Par exemple:memcpy()
,memmove()
,strncpy()
,memset()
,memcmp()
.Ce n'est pas seulement spécifié dans l'introduction à la bibliothèque standard, mais aussi une fois encore dans l'introduction à ces fonctions:
C99 §7.21.1 p2
/C11 §7.24.1 p2
Est-ce intentionnel?
Je ne sais pas si l'UB
%p
avec un pointeur nul est en fait intentionnel, mais puisque la norme indique explicitement que les pointeurs nulles sont considérés comme des valeurs non valides comme arguments des fonctions de bibliothèque standard, puis il spécifie explicitement les cas où un nul pointeur est un argument valide (snprintf, libre, etc.), puis il va et répète encore une fois l'exigence selon laquelle les arguments soient valides même dans zéro cas « n » (memcpy
,memmove
,memset
), alors je pense qu'il est raisonnable de supposer que la Le comité des normes C n'est pas trop préoccupé par le fait que de telles choses ne soient pas définies.la source
%p
n'est pas censé être un comportement indéfiniLes auteurs de la norme C n'ont fait aucun effort pour énumérer de manière exhaustive toutes les exigences comportementales qu'une implémentation doit satisfaire pour convenir à un usage particulier. Au lieu de cela, ils s'attendaient à ce que les personnes qui rédigent des compilateurs exercent un certain sens commun, que la norme l'exige ou non.
La question de savoir si quelque chose invoque UB est rarement utile en soi. Les vraies questions d'importance sont:
Quelqu'un qui essaie d'écrire un compilateur de qualité devrait-il le faire se comporter de manière prévisible? Pour le scénario décrit, la réponse est clairement oui.
Les programmeurs devraient-ils être en droit de s'attendre à ce que des compilateurs de qualité pour tout ce qui ressemble à des plates-formes normales se comportent de manière prévisible? Dans le scénario décrit, je dirais que la réponse est oui.
Certains compilateurs obtus pourraient-ils étirer l'interprétation du Standard afin de justifier de faire quelque chose de bizarre? J'espère que non, mais je ne l'exclurais pas.
Les compilateurs de désinfection devraient-ils se plaindre du comportement? Cela dépendrait du niveau de paranoïa de leurs utilisateurs; un compilateur de désinfection ne devrait probablement pas par défaut se plaindre d'un tel comportement, mais peut-être fournir une option de configuration à faire au cas où les programmes seraient portés vers des compilateurs "intelligents" / stupides qui se comportent de façon étrange.
Si une interprétation raisonnable de la norme impliquerait qu'un comportement est défini, mais que certains rédacteurs de compilateurs étendent l'interprétation pour justifier de faire autrement, est-ce vraiment important ce que dit la norme?
la source
printf("%p", (void*) 0)
comportement est-il indéfini ou pas, selon le Standard? Les appels de fonction profondément imbriqués sont aussi pertinents que le prix du thé en Chine. Et oui, UB est très courant dans les programmes du monde réel - qu'en est-il?%p
chaque signification possible de la question.