65 votes

NULL est-il toujours nul en C ?

Hier, lors d'un entretien pour un poste d'ingénieur logiciel de niveau intermédiaire, un homme a mentionné qu'en C, NULL n'est pas toujours égal à zéro et qu'il avait vu des implémentations du C où NULL n'était pas égal à zéro. Je trouve cela très suspect, mais je veux être sûr. Quelqu'un sait-il s'il a raison ?

(Les réponses n'affecteront pas mon jugement sur ce candidat, j'ai déjà soumis ma décision à mon responsable).

0 votes

0 votes

1 votes

67voto

Oli Charlesworth Points 148744

Je suppose que vous voulez dire le pointeur nul. Il est garanti que la comparaison sera égale à 0 . 1 Mais il ne doit pas nécessairement être représenté par des bits entièrement nuls. 2

Voir également le FAQ comp.lang.c sur les pointeurs nuls.


<p></p><ol><li>Voir C99, 6.3.2.3.</li><li>Il n'y a pas de revendication explicite ; mais voir la note de bas de page pour C99, 7.20.3 (merci à @birryree dans les commentaires).</li></ol>

8 votes

En complément de votre référence à C99, en tant que NULL est toujours égal à 0 (constante de pointeur nulle), la section 7.20.3.2 note également que tous les bits mis à zéro (comme dans le cas de la constante de pointeur nulle) ne sont pas pris en compte. calloc ) n'est pas nécessairement la même que la représentation de 0.0f ou un nombre entier 0 .

0 votes

Génial. C'est donc une pratique assez courante de nos jours, mais pas obligatoire. Merci.

1 votes

Donc, si je convertis un pointeur NULL en un int, puis que je l'imprime, je n'imprimerai pas '0' ? Cela signifie-t-il que le compilateur doit se souvenir du moment où il effectue une comparaison de pointeurs afin de rendre un pointeur NULL égal à zéro ?

14voto

John Bode Points 33046

Le pointeur nul constant est toujours égal à 0. Le site NULL peut être définie par l'implémentation comme une macro nue 0 ou une expression coulée comme (void *) 0 ou toute autre expression entière à valeur nulle (d'où le langage "défini par la mise en œuvre" de la norme).

Le pointeur nul valeur peut être différente de 0. Lorsqu'une constante de type pointeur nul est rencontrée, elle est convertie en une valeur de pointeur nul appropriée.

2 votes

Je suis un peu stupide, mais je crains d'avoir besoin de plus d'éducation maintenant. Quand vous avez un pointeur ptr n'est pas. ptr==NULL exactement la même chose que ptr==0 o !ptr ? Cela dépend-il de la mise en œuvre ?

4 votes

@MrLister : Dans le contexte de votre code source vous avez tout à fait raison. ptr == 0 y ptr == NULL y !ptr sont toutes équivalentes. Cependant, une fois que le code source a été traduit en code machine, la valeur réelle du pointeur nul peut être différente de 0 (et toutes ces comparaisons se feront par rapport à cette valeur réelle du pointeur nul).

0 votes

The null pointer value may be something other than 0 - voulez-vous dire *ptr == something même si void *ptr = 0 ?

14voto

johannes Points 8057

Le § 6.3.2.3 de la norme C99 stipule que

Une expression constante entière avec la valeur 0, ou suc void *, est appelée constante à pointeur nul. Si une constante à pointeur nul est convertie en une constante de type type de pointeur, le pointeur résultant, appelé pointeur nul, est garanti comme n'étant pas égal à un pointeur vers un objet ou une fonction quelconque.

Le § 7.17 dit aussi

[...] NULL qui se développe en une constante pointeur nul définie par l'implémentation [...].

L'adresse du pointeur NULL peut être différente de 0, mais il se comportera comme tel dans la plupart des cas.

(Cela devrait être la même chose que dans les anciens standards C, que je n'ai pas sous la main pour le moment).

10voto

Zack Points 44583

En C, il existe un, et un seul, contexte dans lequel il est nécessaire de convertir explicitement une constante de type pointeur nul en un type de pointeur spécifique pour que le programme fonctionne correctement. Ce contexte est le passage d'un pointeur nul à travers une liste d'arguments de fonction non typée. Dans moderne En C, cela ne se produit que lorsque vous devez passer un pointeur nul à une fonction qui prend un nombre variable d'arguments. (En C traditionnel, cela se produit pour toute fonction non déclarée avec un prototype). execl où le tout dernier argument doit être un pointeur nul explicitement converti en (char *) :

execl("/bin/ls", "ls", "-l", (char *)0);    // correct
execl("/bin/ls", "ls", "-l", (char *)NULL); // correct, but unnecessarily verbose

execl("/bin/ls", "ls", "-l", 0);            // undefined behavior
execl("/bin/ls", "ls", "-l", NULL);         // ALSO undefined behavior

Oui, ce dernier exemple a un comportement non défini. même si NULL est défini comme suit ((void *)0) parce que void * y char * son no implicitement interconvertibles lorsqu'ils sont passés par une liste d'arguments non typée, même s'ils le sont partout ailleurs. (Il existe un langage dans C2011 qui les rend implicitement interconvertibles lorsqu'ils sont passés par une liste d'arguments non typée. va_arg mais ils ont oublié de spécifier que les fonctions de bibliothèque fournies par l'implémentation accèdent aux arguments variadiques comme si elles appelaient va_arg Vous ne pouvez donc vous y fier que pour les fonctions variadiques qui font partie de votre programme. Quelqu'un devrait probablement déposer un DR).

"Sous le capot", le problème ici est que no juste avec le modèle binaire utilisé pour un pointeur nul, mais que le compilateur peut avoir besoin de connaître le type concret exact de chaque argument afin de configurer correctement un cadre d'appel. (Considérez le MC68000, avec ses registres d'adresse et de données séparés ; certains ABIs spécifient que les arguments de pointeur doivent être passés dans les registres d'adresse mais les arguments entiers dans les registres de données. Considérez également toute ABI où int y void * ne sont pas de la même taille. Et c'est de plus en plus rare de nos jours, mais le C prévoit encore explicitement des possibilités de void * y char * n'ayant pas la même taille. (EDIT : je ne suis pas sûr, mais cela n'est peut-être plus autorisé). S'il existe un prototype de fonction, le compilateur peut l'utiliser, mais les fonctions non prototypées et les arguments variadiques n'offrent pas cette assistance.

Le C++ est plus compliqué et je ne me sens pas qualifié pour expliquer comment.

1voto

pizza Points 2855

Dans certaines implémentations, la taille du pointeur n'est pas la même que celle de l'entier. NULL dans un contexte d'entier est 0, mais la disposition binaire réelle ne doit pas nécessairement être tous les 0.

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