Comment écrire du code C / C ++ correctement lorsque le pointeur NULL n’est pas tous les bits nuls

Comme le dit la FAQ comp.lang.c , il existe des architectures dans lesquelles le pointeur null n’est pas tous les bits zéro. Donc, la question est de savoir ce qui vérifie réellement la construction suivante:

void* p = get_some_pointer(); if (!p) return; 

Est-ce que je compare p avec un pointeur nul dépendant de la machine ou je compare p avec zéro arithmétique?

Devrais-je écrire

 void* p = get_some_pointer(); if (NULL == p) return; 

au lieu d’être prêt pour de telles architectures ou est-ce juste ma paranoïa?

Selon la spécification C:

Une expression de type entier avec la valeur 0, ou une expression de ce type exprimée pour taper void *, est appelée constante de pointeur nul. 55) Si une constante de pointeur nul est convertie en un type de pointeur, il est garanti que le pointeur résultant, appelé pointeur nul, se compare différemment à un pointeur sur un object ou une fonction.

Donc 0 est une constante de pointeur nul. Et si nous le convertissons en un type de pointeur, nous obtiendrons un pointeur nul qui pourrait être non-tout-binary-zéro pour certaines architectures. Ensuite, voyons ce que dit la spécification sur la comparaison de pointeurs et d’une constante de pointeur nul:

Si un opérande est un pointeur et que l’autre est une constante de pointeur nul, la constante de pointeur nul est convertie dans le type du pointeur.

Considérons (p == 0) : le premier 0 est converti en un pointeur nul, puis p est comparé à une constante de pointeur nul dont les valeurs de bit réelles dépendent de l’architecture.

Ensuite, voyez ce que dit la spécification sur l’opérateur de négation:

Le résultat de l’opérateur de négation logique! vaut 0 si la valeur de son opérande est différente de 0, 1 si la valeur de son opérande est égale à 0. Le résultat a le type int. L’expression! E est équivalente à (0 == E).

Cela signifie que (!p) est équivalent à (p == 0) qui, selon la spécification, teste p contre la constante de pointeur nul définie par la machine.

Ainsi, vous pouvez écrire en toute sécurité if (!p) même sur des architectures où la constante de pointeur nul n’est pas de tous les bits zéro.

Comme pour C ++, une constante de pointeur nul est définie comme suit:

Une constante de pointeur nul est une expression constante intégrale (5.19) de valeur entière de type entier évaluée à zéro ou une valeur de type std :: nullptr_t. Une constante de pointeur nul peut être convertie en un type de pointeur; le résultat est la valeur de pointeur nul de ce type et se distingue de toutes les autres valeurs du pointeur d’object ou du type de pointeur de fonction.

Ce qui est proche de ce que nous avons pour C, plus le sucre de syntaxe nullptr . Le comportement de l’opérateur == est défini par:

En outre, les pointeurs vers les membres peuvent être comparés, ou un pointeur vers un membre et un pointeur nul constant. Les conversions pointeur sur membre (4.11) et les conversions de qualification (4.4) sont effectuées pour les amener à un type commun. Si un opérande est une constante de pointeur nul, le type commun est le type de l’autre opérande. Sinon, le type commun est un pointeur sur le type de membre similaire (4.4) au type de l’un des opérandes, avec une signature de qualification cv (4.4) qui est l’union des signatures de qualification cv des types d’opérandes. [Remarque: cela implique que tout pointeur sur membre peut être comparé à une constante de pointeur nul. – note finale]

Cela conduit à la conversion de 0 en un type de pointeur (comme pour C). Pour l’opérateur de négation:

L’opérande de l’opérateur de négation logique! est converti contextuellement en bool (article 4); sa valeur est true si l’opérande converti est true et false sinon. Le type du résultat est bool.

Cela signifie que le résultat de !p dépend de la façon dont la conversion d’un pointeur à l’autre est effectuée. La norme dit:

Une valeur nulle, une valeur de pointeur nul ou une valeur de pointeur de membre nul est convertie en false;

Donc if (p==NULL) et if (!p) fait la même chose en C ++.

Cela n’a pas d’importance si le pointeur NULL est de tous les bits zéro ou pas dans la machine réelle. En supposant que p est un pointeur:

 if (!p) 

est toujours un moyen légal de tester si p est un pointeur nul, et c’est toujours équivalent à:

 if (p == NULL) 

Vous pourriez être intéressé par un autre article de C-FAQ: C’est étrange. NULL est garanti à 0, mais le pointeur nul ne l’est pas?


Ci-dessus est vrai pour C et C ++. Notez qu’en C ++ (11), il est préférable d’utiliser nullptr pour le littéral de pointeur nul.

Cette réponse s’applique à C.

Ne mélangez pas NULL avec des pointeurs nuls. NULL est juste une macro garantie pour être une constante de pointeur nul . Une constante de pointeur nul est garantie 0 ou (void*)0 .

De C11 6.3.2.3:

Une expression de type entier avec la valeur 0 ou une expression de ce type exprimée sous la forme void * est appelée constante de pointeur nul 66). Si une constante de pointeur nul est convertie en un type de pointeur, il est garanti que le pointeur résultant, appelé pointeur nul, est différent d’un pointeur sur un object ou une fonction.

66) La macro NULL est définie dans (et les autres en-têtes) sous forme de constante de pointeur nul; voir 7.19.

7.19:

Les macros sont

NUL

qui se développe en une constante de pointeur nul définie par l’implémentation;

Définie par l’implémentation dans le cas de NULL , est soit 0 soit (void*)0 . NULL ne peut pas être autre chose.

Cependant, lorsqu’une constante de pointeur nul est affectée à un pointeur, vous obtenez un pointeur nul , qui peut ne pas avoir la valeur zéro, même s’il est égal à une constante de pointeur nul. Le code if (!p) n’a rien à voir avec la macro NULL , vous comparez un pointeur nul à la valeur arithmétique zéro.

Donc, en théorie, un code comme int* p = NULL peut donner un pointeur nul p différent de zéro.

Dans le passé, les ordinateurs STRATUS avaient des pointeurs nuls de 1 dans toutes les langues.

Cela a causé des problèmes pour C, donc leur compilateur C autoriserait la comparaison des pointeurs 0 et 1 pour renvoyer true

Cela permettrait:

 void * ptr=some_func(); if (!ptr) { return; } 

Pour return sur un ptr nul même si vous pouviez voir que ptr avait une valeur de 1 dans le débogueur

 if ((void *)0 == (void *)1) { printf("Welcome to STRATUS\n"); } 

Serait en fait imprimer “Bienvenue sur STRATUS”

Si votre compilateur est bon, il y a deux choses à surveiller (et seulement deux choses).

1: Les pointeurs statiques par défaut initialisés (c’est-à-dire non affectés) n’auront pas de valeur NULL.

2: memset () sur un struct ou un tableau ou par extension calloc () ne définira pas les pointeurs sur NULL.