NULL est souvent utilisé dans le contexte des pointeurs et est défini via des macros dans plusieurs bibliothèques standard (telles que <iostream>
) pour être l'entier 0
. '\0'
est le caractère nul et correspond à 8 bits de zéros. Par ailleurs, 8 bits de zéros est équivalent à l'entier 0
.
Dans certains cas, bien qu'il soit considéré comme un style horrible, ces deux peuvent être échangés:
int *p='\0';
if (p==NULL) //evaluates to true
cout << "equal\n";
Ou
char a=NULL;
char b='\0';
if (a==b) //evaluates to true
cout << "equal again\n";
Il existe déjà de nombreuses questions similaires sur SO seul; par exemple, la meilleure réponse à cette question ( Quelle est la différence entre NULL, '\ 0' et ) dit "ce n'est pas vraiment la même chose".
Quelqu'un pourrait-il fournir un exemple indiquant que NULL
et \0
ne peut pas être échangé (de préférence une application réelle et non un cas pathologique)?
Quelqu'un pourrait-il fournir un exemple indiquant que NULL et\0 ne peuvent pas être échangés?
La différence entre NULL
et '\0'
Peut affecter la résolution de surcharge.
Exemple ( cochez-le sur Colir ):
#include <iostream>
// The overloaded function under question can be a constructor or
// an overloaded operator, which would make this example less silly
void foo(char) { std::cout << "foo(char)" << std::endl; }
void foo(int) { std::cout << "foo(int)" << std::endl; }
void foo(long) { std::cout << "foo(long)" << std::endl; }
void foo(void*) { std::cout << "foo(void*)" << std::endl; }
int main()
{
foo('\0'); // this will definitely call foo(char)
foo(NULL); // this, most probably, will not call foo(char)
}
Notez que le compilateur gcc utilisé chez Coliru définit NULL
comme 0L
, Ce qui pour cet exemple signifie que foo(NULL)
se résout en foo(long)
plutôt qu'en foo(void*)
. Cette réponse traite de cet aspect en détail.
Leon a raison que lorsqu'il y a plusieurs surcharges pour la même fonction, \0
Préférerait celle qui prend le paramètre de type char
. Cependant, il est important de noter que sur un compilateur typique, NULL
préférerait la surcharge qui prend le paramètre de type int
, pas de type void*
!
Ce qui cause probablement cette confusion, c'est que le langage C permet de définir NULL
comme (void*)0
. La norme C++ indique explicitement (projet N3936, page 444):
Les définitions possibles [de la macro
NULL
] incluent0
Et0L
, Mais pas(void*)0
.
Cette restriction est nécessaire, car par ex. char *p = (void*)0
est C valide mais C++ non valide, tandis que char *p = 0
est valide dans les deux.
Dans C++ 11 et versions ultérieures, vous devez utiliser nullptr
, si vous avez besoin d'une constante nulle qui se comporte comme un pointeur.
Ce code définit plusieurs surcharges d'une même fonction. Chaque surcharge génère 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);
}
On Ideone cette sortie
int
int
char
void*
Je dirais donc que le problème des surcharges n'est pas une application réelle mais un cas pathologique. La constante NULL
se comportera de toute façon mal et devrait être remplacée par nullptr
en C++ 11.
Un autre cas pathologique suggéré par Andrew Keeton à une autre question:
Notez que ce qui est un pointeur nul dans le langage C. Peu importe l'architecture sous-jacente. Si l'architecture sous-jacente a une valeur de pointeur nulle définie comme adresse 0xDEADBEEF, alors c'est au compilateur de trier ce gâchis.
En tant que tel, même sur cette architecture amusante, les moyens suivants sont toujours des moyens valides de rechercher un pointeur nul:
if (!pointer) if (pointer == NULL) if (pointer == 0)
Les méthodes suivantes sont INVALIDES pour rechercher un pointeur nul:
#define MYNULL (void *) 0xDEADBEEF if (pointer == MYNULL) if (pointer == 0xDEADBEEF)
car ceux-ci sont vus par un compilateur comme des comparaisons normales.
Dans l'ensemble, je dirais que les différences sont principalement stylistiques. Si vous avez une fonction qui prend int
et une surcharge qui prend char
, et qu'elles fonctionnent différemment, vous remarquerez une différence lorsque vous les appelez avec \0
Et NULL
constantes. Mais dès que vous placez ces constantes dans des variables, la différence disparaît, car la fonction appelée est déduite du type de la variable.
L'utilisation de constantes correctes rend le code plus facile à gérer et donne une meilleure signification. Vous devez utiliser 0
Lorsque vous voulez dire un nombre, \0
Lorsque vous voulez dire un caractère et nullptr
quand vous voulez dire un pointeur. Matthieu M. souligne dans les commentaires que GCC avait un bug , dans lequel un char*
Était comparé à \0
, Alors que l'intention était de déréférencer le pointeur et de comparer un char
à \0
. De telles erreurs sont plus faciles à détecter si un style approprié est utilisé dans la base de code.
Pour répondre à votre question, il n'y a pas vraiment de cas d'utilisation réel qui vous empêcherait d'utiliser \0
Et NULL
de manière interchangeable. Juste des raisons stylistiques et quelques cas Edge.
Veuillez ne pas faire ça. C'est un anti-modèle, et c'est en fait faux. NULL est pour les pointeurs NULL, '\0'
est le caractère nul. Ce sont logiquement des choses différentes.
Je ne pense pas avoir jamais vu ça:
int* pVal='\0';
Mais cela est assez courant:
char a=NULL;
Mais ce n'est pas une bonne forme. Cela rend le code moins portable et à mon avis moins lisible. Il est également susceptible de provoquer des problèmes dans des environnements mixtes C/C++.
Il repose sur des hypothèses concernant la façon dont une implémentation particulière définit NULL. Par exemple, certaines implémentations utilisent un simple
#define NULL 0
D'autres pourraient utiliser:
#define NULL ((void*) 0)
Et j'en ai vu d'autres se définir comme un entier, et toutes sortes de traitements bizarres.
NULL
devrait, à mon avis, être utilisé uniquement pour indiquer une adresse invalide. Si vous voulez un caractère nul, utilisez '\0'
. Ou définissez ceci comme NULLCHR
. Mais ce n'est pas aussi propre.
Cela rendra votre code plus portable - vous ne commencerez pas à recevoir des avertissements concernant les types, etc. si vous modifiez les paramètres du compilateur/environnement/compilateur. Cela peut être plus important dans un environnement C ou mixte C/C++.
Un exemple d'avertissements pouvant survenir: Considérez ce code:
#define NULL 0
char str[8];
str[0]=NULL;
Cela équivaut à:
#define NULL 0
char str[8];
str[0]=0;
Et nous attribuons une valeur entière à un caractère. Cela peut provoquer un avertissement du compilateur, et s'il y a suffisamment d'occurrences de cela, vous ne verrez bientôt aucun avertissement important. Et pour moi, c'est le vrai problème. Avoir des avertissements dans le code a deux effets secondaires:
Dans les deux cas, les bogues réels peuvent alors passer, qui seraient détectés par le compilateur si nous avions pris la peine de lire les avertissements (ou d'activer -Werror)
Oui, ils peuvent présenter un comportement différent lors de la résolution des fonctions surchargées.
func('\0')
invoque func(char)
,
tandis que
func(NULL)
invoque func(integer_type)
.
Vous pouvez supprimer la confusion en utilisant nullptr , qui est toujours un type de pointeur, n'affiche aucune ambiguïté lorsque attribue/compare la valeur ou la résolution de surcharge de fonction .
char a = nullptr; //error : cannot convert 'std::nullptr_t' to 'char' in initialization
int x = nullptr; //error : nullptr is a pointer not an integer
Notez que, il est toujours compatible avec NULL:
int *p=nullptr;
if (p==NULL) //evaluates to true
Extrait du livre C++ Programming Stroustrup 4th Edition:
Dans un code plus ancien, 0 ou NULL est généralement utilisé à la place de nullptr (§7.2.2). Cependant, l'utilisation de nullptr élimine la confusion potentielle entre les entiers (tels que 0 ou NULL) et les pointeurs (tels que nullptr).
Les programmes informatiques ont deux types de lecteurs.
Le premier type sont des programmes informatiques, comme le compilateur.
Le deuxième type sont les humains, comme vous et vos collègues.
Les programmes sont généralement bien avec l'obtention d'un type de zéro à la place d'un autre. Il y a des exceptions, comme l'ont souligné les autres réponses, mais ce n'est pas vraiment important.
Ce qui est important, c'est que vous jouez avec les lecteurs humains .
Les lecteurs humains sont très sensibles au contexte. En utilisant le mauvais zéro, vous êtes mentir pour vous lecteurs humains. Ils vont te maudire.
Un être humain menti peut ignorer plus facilement les bogues.
Un humain à qui on a menti peut voir des "bogues" qui ne sont pas là. Lors de la "correction" de ces bogues phanthom, ils introduisent de vrais bogues.
Ne mentez pas à vos humains. L'un des humains auxquels vous mentez est votre futur moi. Tu vas aussi te maudire.
Extraits du projet N3936 en C++ 14:
18.2 Types [support.types]
3 La macro
NULL
est une constante de pointeur nul C++ définie par l'implémentation dans la présente Norme internationale (4.10).4.10 Conversions de pointeur [conv.ptr]
1 A constante de pointeur nul est un littéral entier (2.14.2) de valeur 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 le valeur du pointeur nul de ce type et se distingue de toutes les autres valeurs du type pointeur objet ou pointeur fonction.
Ainsi, NULL
peut être n'importe quel littéral entier de valeur zéro, ou une valeur de type std::nullptr_t
comme nullptr
, tandis que '\0'
est toujours le littéral zéro à caractère étroit.
Donc, pas en général interchangeables, même si dans un contexte de pointeur, vous ne pouvez voir qu'une différence stylistique.
Un exemple serait:
#include <iostream>
#include <typeinfo>
int main() {
std::cout << typeid('\0').name << '\n'
<< typeid(NULL).name << '\n'
<< typeid(nullptr).name << '\n';
}
Selon la référence C/C++, NULL est défini comme une macro qui se développe en une constante de pointeur null. Ensuite, nous pouvons lire qu'une constante de pointeur nul peut être convertie en n'importe quel type de pointeur (ou type pointeur sur membre), qui acquiert une valeur de pointeur nulle. Il s'agit d'une valeur spéciale qui indique que le pointeur ne pointe vers aucun objet.
Définition se référant à C:
Une constante pointeur nul est une expression constante intégrale qui est évaluée à zéro (comme 0 ou 0L), ou le transtypage de cette valeur pour taper void * (comme (void *) 0).
Définition faisant référence à C++ 98:
Une constante pointeur nul est une expression constante intégrale qui est évaluée à zéro (telle que 0 ou 0L).
Définition faisant référence à C++ 11:
Une constante de pointeur nul est soit une expression constante intégrale évaluée à zéro (telle que 0 ou 0L), soit une valeur de type nullptr_t (telle que nullptr).
Supposons que nous avons les méthodes suivantes:
class Test {
public:
method1(char arg0);
method1(int arg0);
method1(void* arg0);
method1(bool arg0);
}
L'appel de method1 avec l'argument NULL
ou nullptr
doit appeler method1(void* arg0);
. Cependant, si nous appelons method1 avec l'argument '\0'
Ou 0
, Il faut exécuter method1(char arg0);
et method1(int arg0);
.