29 votes

L'expression "(ptr == 0)! = (Ptr == (void *) 0)" peut-elle vraiment être vraie?

J'ai lu cette demande dans un fil du forum lié à un commentaire de @jsantander:

Gardez à l'esprit que lorsque vous affectez ou comparer un pointeur à zéro, il y a quelque chose de magique qui se passe derrière les coulisses d'utiliser le bon modèle pour le pointeur (qui peut ne pas être égale à zéro). C'est une des raisons pourquoi les choses comme #define NULL (void*)0 sont mal – si vous comparez un char* de NULL que la magie a été explicitement (et probablement sans le savoir) hors tension, et un résultat non valide peut arriver. Juste pour être clair:

(my_char_ptr == 0) != (my_char_ptr == (void*)0)

Donc, la façon dont je le comprends, pour une architecture où le pointeur NULL est, disons, 0xffff, le code if (ptr), de comparer ptr pour 0xffff au lieu de 0.

Est-ce vraiment vrai? Est-il décrit par la norme C++?

Si cela est vrai, cela voudrait dire que la valeur 0 peut être utilisée en toute sécurité, même pour les architectures qui ont non nulle d'une valeur de pointeur NULL.

Modifier

Comme un supplément de clarification, d'envisager ce code:

char *ptr;
memset(&ptr, 0, sizeof(ptr));
if ((ptr == (void*)0) && (ptr != 0)) {
    printf("It can happen.\n");
}

C'est la façon dont je comprends la demande de ce post sur le forum.

34voto

Matt McNabb Points 14273

Il y a deux volets à votre question. Je vais commencer par:

Si cela est vrai, cela voudrait dire que la valeur 0 peut être utilisée en toute sécurité, même pour les architectures qui ont non nulle d'une valeur de pointeur NULL.

Vous mélangez "valeur" et de "représentation". La valeur d'un pointeur null est appelé la valeur de pointeur null. La représentation est les bits de mémoire qui sont utilisés pour stocker cette valeur. La représentation d'un pointeur null peut être n'importe quoi, il n'est pas nécessaire que c'est tous les bits à zéro.

Dans le code:

char *p = 0;

p est garanti pour être un pointeur null. Il pourrait ne pas avoir tous les bits à zéro.

Ce n'est pas plus "magique" que le code:

float f = 5;

f n'ont pas la même représentation (bit-modèle en mémoire) que l'int 5 n', mais il n'y a pas de problème.

Le standard C++ définit ce. Le texte quelque peu changé en C++11 avec l'ajout d' nullptr; cependant, dans toutes les versions de C et C++, l'entier littéral 0 lors de la conversion d'un pointeur de type génère un pointeur null.

À Partir De C++11:

Un pointeur null constante est un élément constant de l'expression prvalue de type entier qui prend la valeur zéro ou un prvalue de type std::nullptr_t. Un pointeur null constante peut être converti en un type pointeur; le résultat est la valeur de pointeur null de ce type et se distingue de tous les autres de la valeur de l'objet pointeur de fonction ou de type pointeur. Une telle conversion est appelé un pointeur null conversion.

0 est un pointeur null constante, et (char *)0 , par exemple, est un pointeur null valeur de type char *.

Il est indifférent qu'un pointeur null a tous les bits à zéro ou pas. Ce qui importe, ce est que un pointeur null est garanti d'être généré lorsque vous convertissez une partie intégrante constexpr de valeur 0 d'un type pointeur.

Le déplacement sur l'autre partie de votre question. Le texte que vous avez cité est complet ordures à travers et à travers. Il n'y a pas de "magie" dans l'idée que la conversion entre les types de résultats dans une représentation différente, comme je l'ai discuter ci-dessus.

Le code my_char_ptr == NULL est garanti pour tester si oui ou non my_char_ptr est un pointeur null.

Il serait mal si vous écrivez dans votre propre code source, #define NULL (void*)0. C'est parce que c'est un comportement indéterminé de définir une macro qui pourrait être défini par un en-tête standard.

Toutefois, la norme en-têtes peuvent écrire ce qu'ils veulent, de sorte que les exigences de la Norme pour les pointeurs null sont remplies. Les compilateurs de "magie" dans l'en-tête standard code; par exemple il ne doit pas être un fichier appelé iostream sur le système de fichiers; le compilateur peut voir #include <iostream> puis sont codées en dur toutes les informations que la Norme exige iostream publier. Mais pour des raisons pratiques évidentes, les compilateurs n'ont généralement pas le faire; ils permettent d'envisager la possibilité pour les équipes afin de développer la bibliothèque standard.

De toute façon, si un compilateur C++ inclut #define NULL (void *)0 dans son propre en-tête, et comme un résultat de quelque chose de non-conformité est le cas, alors le compilateur serait non-conforme, évidemment. Et si de rien non conforme arrive, alors il n'y a pas de problème.

Je ne sais pas qui est le texte que vous citez de diriger son "c'est mal" commentaire. Si elle est destinée au compilateur de vendeurs en leur disant de ne pas être "mal" et mettre hors d'état de non-conformité des compilateurs, je suppose qu'on ne peut pas argumenter avec ça.

12voto

Steve Jessop Points 166970

Je pense que le post sur le forum vous lien est incorrect (ou que nous avons mal interprété ce qu'il entend par !=). Les deux sous-expressions ont des sémantiques différentes, mais le même résultat. En supposant que l' my_char_ptr a vraiment de type char* ou similaire, et une valeur valide:

my_char_ptr == 0 convertit 0 pour le type d' my_char_ptr. Qui donne un pointeur null, car 0 est un exemple d'un soi-disant "pointeur null constante", qui est défini dans la norme. Il compare ensuite les deux. La comparaison est vraie si et seulement si my_char_ptr est un pointeur null, parce que seuls les pointeurs null comparer égal à d'autres pointeurs null.

my_char_ptr == (void*)0 convertit my_char_ptr de void*, et compare ensuite que le résultat de la conversion d' 0 de void* (qui est un pointeur null). La comparaison est vraie si et seulement si my_char_ptr est un pointeur null parce que quand vous convertir un pointeur vers void* le résultat est un pointeur nul si et seulement si la source est un pointeur null.

La question de savoir si les pointeurs null, sont représentées avec 0 bits ou pas, c'est intéressant, mais sans pertinence pour l'analyse du code.

La pratique de danger de penser qu' NULL est un pointeur null (plutôt que de simplement un pointeur null constante) est ce que vous pourriez penser qu' printf("%p", NULL) a défini le comportement, ou qu' foo(NULL) qui fera appel à la void* de surcharge de foo plutôt que de l' int de surcharge, et ainsi de suite.

8voto

Damon Points 26437

Non, parce qu'ils incidentially utilisé le seul cas où il est garanti pour fonctionner comme exemple.
Sinon, oui.

Bien que practially vous probablement ne verrez pas la différence, à proprement parler, la préoccupation est correct.

La norme C++ exige (4.10) que:

  1. Un pointeur null constante (ce qui est une partie intégrante de la constante expression qui est évaluée 0, ou un prvalue de type std::nullptr_t) convertit le pointeur null en tout genre.
  2. Deux pointeurs null du même type de comparer l'égalité.
  3. Un prvalue de type pointeur-de-cv-T peut être converti en pointeur-de-cv-nulle, et la valeur de pointeur null sera ajusté en conséquence.
  4. Les pointeurs de classes dérivées peuvent être convertis à des pointeurs de classes de base, et la valeur de pointeur null sera ajusté en conséquence.

Cela signifie que, si vous êtes pointilleux sur les termes, que les pointeurs nuls de void et char et foo_bar sont non seulement pas nécessairement zéro modèles de bits, mais également sont pas nécessairement les mêmes. Seulement null pointeurs de même type sont nécessairement les mêmes (et en fait, même pas, c'est vrai, il est dit qu'ils doivent comparer l'égalité, ce qui n'est pas la même chose).

Le fait qu'il dit explicitement "La valeur de pointeur null est convertie en une valeur de pointeur null de la type de destination" signifie que ce n'est pas seulement absurde, théorique contorsion de la formulation, mais en effet conçu comme un légitime de la mise en œuvre.

C'est indépendamment du fait que le même littéral 0 se convertir à l'pointeur null de chaque type.

Incidentially, à leur exemple, ils ont comparé à l' void*, ce qui permettra de travailler en raison de la règle de conversion. Aussi, dans la pratique, le pointeur null pour chaque type est un zéro séquence de bits sur chaque architecture que vous êtes susceptible de rencontrer dans votre vie (même si bien sûr, ce n'est pas garanti).

7voto

James Kanze Points 96599

Tout d'abord, je ne suis pas sûr qu' (charPtr == 0) != (charPtr == (void*)0) est autorisé, même en C++. Dans les deux cas, vous êtes la conversion d'un pointeur null constant (0) à un pointeur, ce qui résultats dans un pointeur null. Et tous les pointeurs null comparez de l'égalité.

Deuxièmement, alors que je ne connais pas le contexte de ce passage que vous citez, vous n'avez pas vraiment à vous soucier NULL être (void*)0: le code de l'utilisateur ne peut pas définir juridiquement NULL (au moins pas si c' inclut tous les en-têtes standard), et la norme C++ nécessite NULL être défini comme un pointeur null constante; c'est à dire un constant expression intégrale de l'évaluation à 0. (Notez que en dépit de son nom, un pointeur null constante ne peut pas avoir un pointeur type.) Il pourrait donc être 0 (plus ou moins standard définition, depuis les débuts de C), ou, éventuellement, 0L, ou même (1-1), mais pas ((void*)0). (Bien sûr, il pourrait aussi être quelque chose comme __nullptr, un compilateur intégré constante qui prend la valeur integer 0, mais déclenche un avertissement immédiatement convertis en un pointeur null.

Enfin: il n'y a pas besoin de pointeur null ont tous 0 bits, et il y a certainement eu des cas où ce n'était pas le cas. D'autre part, il est nécessaire que la comparaison d'un pointeur null null le pointeur constant évaluer vrai; c'est le compilateur pour le faire fonctionner. Et depuis NULL est nécessaire pour être défini comme un pointeur null constant, si vous utilisez NULL ou 0 est purement une question de de préférence personnelle et de la convention.

EDIT:

Juste pour clarifier un peu: le point critique implique la conversion d'un pointeur null constante", partie intégrante de l'expression constante l'évaluation à 0. Ce qui peut surprendre les gens est:

int zero = 0;       //  NOT a constant expression.
void* p1 = reinterpret_cast<void*>( zero );
void* p2 = 0;
if ( p1 == p2 )     //  NOT guaranteed!

Les résultats de la conversion d'un non-expression constante qui prend la valeur de zéro à un pointeur est pas garanti d'être un null pointeur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X