Est ((void*)0) un pointeur null constante?
Je suis en train de lire ce blog et en vertu de la section pointeur Null constantes et les expressions entre parenthèses l'auteur des références § 6.3.2.3 et § 6.5.1 de l'ISO C standard et dit:
Il n'est pas dire qu'une mise entre parenthèses de pointeur null constante est un pointeur null constante.
Ce qui implique, à proprement parler, que
(void*)0
est un pointeur null
constante, mais((void*)0)
ne l'est pas.
Alors:
Je suis sûr que la plupart des implémentations C ne traiter une mise entre parenthèses de pointeur null constante comme un pointeur null constante, et de définir
NULL
soit comme0
,((void*)0)
, ou de quelque autre manière.
Les deux sections citées dire:
§ 6.3.2.3
Une constante entière expression avec la valeur 0, ou une expression
fonte de type void *, est appelé un pointeur null constante.
§ 6.5.1
Une mise entre parenthèses de l'expression est une expression primaire. Son type et sa valeur
sont identiques à ceux de la sans parenthèse expression. C'est un
lvalue, une fonction de désignation, ou un vide d'expression si l'
sans parenthèse expression est, respectivement, une lvalue, une fonction
indicateur, ou un vide d'expression.
N'est pas la phrase en gras contredire le grief de l'auteur qui ((void*)0)
n'est pas un pointeur null constante?
- J'ai pris la liberté d'ajouter de la "langue-avocat" tag. (Et c'est agréable de savoir que quelqu'un lit mon blog!)
- Connexes Est-il possible d'avoir un pointeur littérale?
Vous devez vous connecter pour publier un commentaire.
Non, il ne le fait pas. (J'avoue être un peu biaisé, car le référencés blog est le mien.)
La phrase en gras indique que son type et valeur sont identiques à ceux de la sans parenthèse expression. Ce n'est pas assez à entendre que c'est un pointeur null constante.
Considérer:
(void*)0
est un pointeur null constante.((void*)0)
a le même type et de même valeur que(void*)0
.var
aussi a le même type et de même valeur que(void*)0
, maisvar
n'est clairement pas un pointeur null constante.Cela dit, je suis 99+% sûr que le intention est que
((void*)0)
est un pointeur null constante, et plus généralement toute mise entre parenthèses de pointeur null constante est un pointeur null constante. Les auteurs de la norme simplement négligé de le dire. Et depuis la description de la mise entre parenthèses des expressions en 6.5.1p5 spécifiquement énumère plusieurs autres caractéristiques qui sont héritées par les expressions entre parenthèses:l'omission est troublant (mais seulement légèrement, donc).
Mais supposons, pour les besoins de la discussion, que
((void*)0)
n'est pas un pointeur null constante. Quelle différence cela fait-il?(void*)0
est un pointeur null constante, dont la valeur est un pointeur null de typevoid*
, donc par la sémantique des expressions entre parenthèses((void*)0)
a aussi une valeur qui est un pointeur null de typevoid*
. Les deux(void*)0
et((void*)0)
sont adresse constantes. (Eh bien, je pense ils sont.) Donc quels contextes nécessitent un pointeur null constante et de ne pas accepter une constant adresse? Il y a seulement quelques.6.5.9 opérateurs d'Égalité
Une expression de la fonction de type pointeur peut être comparé pour l'égalité à un pointeur null constante. (Un pointeur d'objet peut être comparé à une expression de type
void*
, mais un pointeur de fonction ne peuvent pas, sauf si c'est un pointeur null constante.) Donc:serait une violation de contrainte.
6.5.16.1 Simple affectation
Dans une affectation, un pointeur null constante peut être affecté à un objet de pointeur de fonction type, et sera converti implicitement. Une expression de type
void*
ce n'est pas un pointeur null constante ne peut pas être attribué à un pointeur de fonction. Les mêmes contraintes s'appliquent à l'argument de passage et d'initialisation. Donc:serait une violation de contrainte si
((void*)0)
n'étaient pas un pointeur null constante. Merci à l'intervenant hvd pour trouver ce.7.19 définitions Communes
<stddef.h>
La macro
NULL
étend à "une mise en œuvre définies par pointeur null constante". Si((void*)0)
n'est pas un pointeur null constante, alors ceci:serait invalide. Ce serait une restriction imposée à la mise en œuvre, et non pas sur les programmeurs. Notez que ceci:
est certainement pas valide, puisque les macro-définitions dans la norme en-têtes doivent être pleinement protégés par des parenthèses si nécessaire (7.1.2p5). Sans les parenthèses, l'expression valide
sizeof NULL
serait une erreur de syntaxe, de l'expansionsizeof (void*)
suivie par un parasite constante0
.void* var
a la faiblesse devar
n'est pas un pointeur null constant, car il arrive aussi de ne pas être un GLACE.var
n'en est pas un car il n'est pas une constante, mais que signifient-ils pour((void*)0)
de ne pas l'être? Est-il une sorte de pervers comportement du compilateur qui est techniquement autorisé par les spécifications en raison de cet oubli?weird
n'a pas besoin d'être un pointeur null à tous, si que la conversion ne se fait pas parce que((void*)0)
n'est pas un pointeur null constante dans la première place. Et siweird
n'est pas un pointeur null, alors il n'a pas besoin de comparer égal à un. Naturellement, cela est un non-sens si l'acte de l'ajout de parenthèses pour(void*)0
constitue en soi une conversion d'un type pointeur, ce qui n'est probablement si((void*)0)
est de type pointeur.((void*)0)
est pas un pointeur null constante, mais est un pointeur null, alors 6.3.2.3.4, il semble que tout est bien, car on peut toujours convertir en un autre type de pointeur.void *weird = ((void*)0);
faitwierd
un pointeur null, parce que même si((void*)0)
n'étaient pas un null le pointeur constant (qui je crois) il doit être un pointeur null initialiseur, parce que ", Son type et sa valeur sont identiques à ceux de la sans parenthèse expression". Et parce que le sans parenthèse expression va réussir à fairewierd
unNULL
pointeur, la mise entre parenthèses d'un.((void*)0)
à un pointeur de type de fonction est suffisant pour démontrer le problème, même sans comparaisons:void (*fp)(void) = ((void*)0);
est valide si et seulement si((void*)0)
est un PNJ. Peut-être aussi intéressant, c'est que le C++ a remplacé la sémantique des parenthèses avec une autre beaucoup plus simple: "La mise entre parenthèses de l'expression peut être utilisé exactement dans les mêmes contextes que ceux où l'expression entre crochets peuvent être utilisés, et avec le même sens, sauf indication contraire." Cela concerne, par exemple,char str[] = ("abc");
, ce qui est valable en C++ mais pas en C.sizeof NULL
où l'absence de parenthèses sur(void*)0
serait un problème? Est-il une raisonsizeof NULL
devrait s'attendre à avoir un sens tout à fait particulier, puisque les différents types de pointeurs sont autorisés à avoir des tailles différentes représentations?#define NULL (void*)0
. Je considère que c'est de la chance que les auteurs de la norme ne sont pas arbitrairement desserrer des exigences parce qu'ils présument que certains de construire ne serait pas utile ou significative, en particulier lorsque ces exigences imposent essentiellement à coût zéro. Pouvez-vous penser à une raison quelconque, même s'il lui était permis, d'avoir#define NULL (void*)0
plutôt que#define NULL ((void*)0)
?NULL
est un pointeur null constante, par définition. J'avais défenseur de la fixation de la description des expressions entre parenthèses pour préciser que((void*)0)
est une définition valable pourNULL
. Modification de la norme pour permettre(void*)0
comme une définition valable pourNULL
serait ridicule.(void*)0
être observable différente de((void*)0)
? Sur les systèmes où les différents types de pointeurs ont des tailles différentes, ayant un compilateur affichez àsizeof NULL
semble mieux que d'avoir à rendre compte de quelque chose qui peut ne pas être la taille l'un est intéressé. Dans quel autre contexte aurait(void*)0
comportement observable différemment de((void*)0)
?sizeof NULL
-- et autant que je sache, compilateurs existants n'ont pas un problème avec elle. Cela ne signifie pas que vous devez utiliser dans votre code. Les compilateurs peuvent avertir à propos de ce qu'ils veulent.C'est une mise entre parenthèses de l'expression qui contient un pointeur null constante, de sorte qu'il est incontestablement une valeur de pointeur null. En l'utilisant comme une rvalue a exactement le même effet que l'utilisation de la "conformité" de la version en tant que r-valeur.
Si il y avait quelques règles syntaxiques qui pourrait seulement accepter un pointeur null constante, il ne serait pas admissible. Mais je ne suis pas au courant de tout (même si je suis moins expert sur C).
Et alors que ni l'un est un constante (en se référant à la grammaire formelle de la production), les deux peuvent apparaître dans une expression constante dans un initialiseur, parce que les deux pointeur null constantes et l'adresse constantes sont autorisés, et une constante de la valeur de pointeur null est explicitement inclus dans la catégorie des constant adresse.
Pointeur de comparaisons aussi expressément mention de pointeur null constantes... mais ici, les valeurs de pointeur sont également acceptées, et tous pointeur null valeurs sont traités de manière égale. De même pour le ternaire et l'affectation des opérateurs.
S'il vous plaît ne soyez conscient que ces règles sont assez différents, en C++, où les deux expressions ci-dessus sont en constante pointeur null valeurs de type
void*
, mais pas universel pointeur null constantes. Pointeur Null constantes en C++ sont partie intégrante des expressions constantes qui s'évaluer à zéro. Etvoid*
ne pas convertir implicitement à d'autres types de pointeur.(void*)0
ou((void*)0)
] une constante"? La norme revendications explicitement que(void*)0
est un pointeur null constante.((void*)0)
, si ce n'était pas un PNJ ne serait pas. Une expression de pointeur de type de fonction peut être comparé pour l'égalité ou de l'inégalité à un pointeur null constante, mais pas à toute autre expression constante de typevoid*
. Voir ma réponse mis à jour pour plus de détails.Essayez d'imprimer ligne ci-dessous dans votre code C:
printf("%p",(void*)0);
Vous obtiendrez la sortie:
(néant)
(char*)0
, ou(void*){0}
mais(char*)0
et(void*){0}
ne sont pas de pointeur null constantes tandis que(void*)0
est.