Définition de la macro NULL en C++
Léon a raison que lorsqu'il y a plusieurs surcharges pour la même fonction, \0
préfère celui qui prend un paramètre de type char
. Cependant, il est important de noter que sur un compilateur typique, NULL
préférerait la surcharge qui prend un paramètre de type int
non de type void*
!
Ce qui cause probablement cette confusion est que le langage C permet de définir NULL
comme (void*)0
. La norme C++ indique explicitement (projet N3936, page 444) :
Définitions possibles [de la macro NULL
] comprennent 0
y 0L
mais pas (void*)0
.
Cette restriction est nécessaire, car par exemple char *p = (void*)0
est un C valide mais un C++ invalide, alors que char *p = 0
est valable dans les deux.
En C++11 et plus, vous devez utiliser nullptr
si vous avez besoin d'une constante nulle qui se comporte comme un pointeur.
Comment la suggestion de Léon fonctionne en pratique
Ce code définit plusieurs surcharges d'une même fonction. Chaque surcharge donne le type du paramètre :
#include <iostream>
void f(int) {
std::cout << "int" << std::endl;
}
void f(long) {
std::cout << "long" << std::endl;
}
void f(char) {
std::cout << "char" << std::endl;
}
void f(void*) {
std::cout << "void*" << std::endl;
}
int main() {
f(0);
f(NULL);
f('\0');
f(nullptr);
}
Sur Ideone ces sorties
int
int
char
void*
Je prétends donc que le problème des surcharges n'est pas une application réelle mais un cas pathologique. Le site NULL
se comportera mal de toute façon, et devrait être remplacé par nullptr
en C++11.
Que se passe-t-il si NULL n'est pas égal à zéro ?
Un autre cas pathologique est suggéré par Andrew Keeton à une autre question :
Notez que ce qu'est un pointeur nul dans le langage C. Cela n'a pas d'importance sur l'architecture sous-jacente. Si l'architecture sous-jacente a une valeur de pointeur nul définie comme l'adresse 0xDEADBEEF, alors c'est au compilateur de régler ce problème.
Ainsi, même sur cette drôle d'architecture, les méthodes suivantes sont toujours valables pour vérifier la présence d'un pointeur nul :
if (!pointer)
if (pointer == NULL)
if (pointer == 0)
Les méthodes suivantes sont INVALIDES pour vérifier la présence d'un pointeur nul :
#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)
car elles sont vues par un compilateur comme des comparaisons normales.
Résumé
Dans l'ensemble, je dirais que les différences sont surtout stylistiques. Si vous avez une fonction qui prend int
et une surcharge qui prend char
et ils fonctionnent différemment, vous remarquerez la différence lorsque vous les appelez avec \0
y NULL
constantes. Mais dès que l'on place ces constantes dans des variables, la différence disparaît, car la fonction qui est appelée est déduite du type de la variable.
L'utilisation de constantes correctes rend le code plus facile à maintenir et transmet mieux le sens. Vous devez utiliser 0
quand vous voulez dire un nombre, \0
quand vous voulez dire un personnage, et nullptr
quand vous voulez dire un pointeur. Matthieu M. fait remarquer dans les commentaires, que GCC avait un bug dans lequel un char*
a été comparé à \0
alors que l'intention était de déréférencer le pointeur et de comparer une valeur de char
a \0
. De telles erreurs sont plus faciles à détecter, si un style approprié est utilisé dans toute la base de code.
Pour répondre à votre question, il n'y a pas vraiment de cas d'utilisation réelle qui vous empêcherait d'utiliser \0
y NULL
de manière interchangeable. Ce ne sont que des raisons stylistiques et quelques cas limites.
22 votes
(
-Wzero-as-null-pointer-constant
)0 votes
On voit ça tout le temps, et c'est un anti-modèle terrible.
3 votes
Biffen, mjs, ma question est sincère - j'essaie de comprendre la différence entre les deux. Voulez-vous dire que les deux sont équivalents et que c'est juste une question de style (ce qui, je suppose, est le but de l'indicateur gcc) ?
0 votes
Vous pouvez vous y référer : stackoverflow.com/questions/13091804/
0 votes
@cwest - non, c'est plus que du style, j'ai vu des problèmes réels causés par cela. Je mettrai à jour ma réponse avec des exemples plus tard.
17 votes
Utiliser C++11 &
nullptr
pasNULL
0 votes
Certaines versions du compilateur permettent
virtual void foo () = NULL;
mais personne ne permettra'\0'
à la place deNULL
. Par conséquent, l'interchangeabilité desNULL
&'\0'
est dépendant du compilateur, et doit donc être évité.11 votes
Ver Bugs trouvés dans GCC avec l'aide de PVS-Studio l'analyseur statique a trouvé un bogue probable car
char*
a été comparé à\0
ce qui rendait probable que l'auteur ait oublié de déréférencer le pointeur (pour obtenir une valeur dechar
) avant de procéder à la comparaison. Une saisie correcte donne du pouvoir aux outils.1 votes
int *p='\0';
c'est fr.wikipedia.org/wiki/Not_even_wrong .2 votes
@cwest Au fait, bienvenue sur StackOverflow, et une bonne première question, bien écrite.
1 votes
Ce qui suit est hors contexte, mais peut en valoir la peine. Dans les bases de données SQL, comme Oracle, il y a deux choses distinctes qui doivent être exprimées. La première est la chaîne de caractères de longueur variable de longueur zéro. La seconde est l'absence de valeur, appelée NULL en SQL. Si vous choisissez la même représentation pour ces deux choses, vous créez un désordre. Oracle a créé un tel désordre, il y a plus de 25 ans. Le NULL SQL n'est pas exactement la même chose que le NULL C++, mais ils sont conceptuellement proches.
0 votes
Quand j'ai vu le titre de la question, j'ai cru qu'il s'agissait de
0
yNULL
.0 votes
@cwest C'était juste ma façon de dire (paresseusement) que vous devriez toujours utiliser
nullptr
pour les pointeurs nuls, jamaisNULL
,0
o\0
. C'est légèrement hors sujet, car cela invalide en quelque sorte la question.