Utilisez-vous NULL ou 0 (zéro) pour les pointeurs en C ++?

Dans les premiers jours de C ++, quand il était boulonné sur C, vous ne pouviez pas utiliser NULL car il était défini comme (void*)0 . Vous ne pouvez pas assigner NULL à un pointeur autre que void* , ce qui le rend inutile. À cette époque, il était accepté que vous utilisiez 0 (zéro) pour les pointeurs nuls.

Jusqu’à ce jour, j’ai continué à utiliser zéro comme pointeur nul, mais ceux qui m’entourent insistent pour utiliser NULL . Personnellement, je ne vois aucun avantage à donner un nom ( NULL ) à une valeur existante – et comme j’aime aussi tester les pointeurs comme des valeurs de vérité:

 if (p && !q) do_something(); 

alors utiliser zéro a plus de sens (comme si vous utilisez NULL , vous ne pouvez pas utiliser logiquement p && !q – vous devez comparer explicitement avec NULL , sauf si vous supposez que NULL est égal à zéro, auquel cas utilisez NULL ).

Y a-t-il une raison objective de préférer zéro à NULL (ou vice versa), ou est-ce que tout est juste une préférence personnelle?

Edit: Je devrais append (et dire à l’origine) qu’avec RAII et les exceptions, j’utilise rarement des pointeurs nuls / nuls, mais parfois vous en avez toujours besoin.

Voici la version de Stroustrup: C ++ Style and Technique FAQ

En C ++, la définition de NULL est 0, il n’y a donc qu’une différence esthétique. Je préfère éviter les macros, donc j’utilise 0. Un autre problème avec NULL est que les gens pensent parfois à tort qu’il est différent de 0 et / ou pas un entier. Dans le code pré-standard, NULL était / est parfois défini sur quelque chose d’inadapté et donc / a dû être évité. C’est moins courant ces jours-ci.

Si vous devez nommer le pointeur null, appelez-le nullptr ; c’est ce que l’on appelle en C ++ 11. nullptr sera alors un mot-clé.

Cela dit, ne transpire pas les petites choses.

Il y a quelques arguments (dont l’un est relativement récent) qui, à mon avis, contredisent la position de Bjarne à ce sujet.

  1. Documentation d’intention

L’utilisation de NULL permet d’effectuer des recherches sur son utilisation et indique également que le développeur a voulu utiliser un pointeur NULL , qu’il soit ou non interprété par le compilateur.

  1. Surcharge de pointeur et ‘int’ est relativement rare

L’exemple que tout le monde cite est:

 void foo(int*); void foo (int); void bar() { foo (NULL); // Calls 'foo(int)' } 

Cependant, du moins à mon avis, le problème avec ce qui précède n’est pas que nous utilisons NULL pour la constante de pointeur nul, c’est que nous avons des surcharges de ‘foo’ qui prennent des types d’arguments très différents. Le paramètre doit également être un int , car tout autre type entraînera un appel ambigu et générera ainsi un avertissement utile sur le compilateur.

  1. Les outils d’parsing peuvent aider AUJOURD’HUI!

Même en l’absence de C ++ 0x, il existe aujourd’hui des outils qui vérifient que NULL est utilisé pour les pointeurs, et que 0 est utilisé pour les types intégraux.

  1. C ++ 11 aura un nouveau type std::nullptr_t .

C’est l’argument le plus récent de la table. Le problème de 0 et NULL est activement pris en compte pour C ++ 0x, et vous pouvez garantir que pour chaque implémentation fournissant NULL , la première chose à faire est de:

 #define NULL nullptr 

Pour ceux qui utilisent NULL plutôt que 0 , le changement sera une amélioration de la sécurité de type avec peu ou pas d’effort – s’il ya lieu, il peut aussi y avoir quelques bogues où ils ont utilisé NULL pour 0 . Pour quiconque utilise 0 aujourd’hui … euh … eh bien j’espère qu’ils ont une bonne connaissance des expressions régulières …

Utilisez NULL. NULL montre votre intention. Que ce soit 0 est un détail d’implémentation qui ne devrait pas avoir d’importance.

J’ai arrêté d’utiliser NULL en faveur de 0 il y a longtemps (ainsi que la plupart des autres macros). Je l’ai fait non seulement parce que je voulais éviter autant que possible les macros, mais aussi parce que NULL semble être trop utilisé dans le code C et C ++. Il semble être utilisé chaque fois qu’une valeur de 0 est nécessaire, pas seulement pour les pointeurs.

Sur les nouveaux projets, je mets ceci dans un en-tête de projet:

 static const int nullptr = 0; 

Maintenant, lorsque les compilateurs compatibles C ++ 0x arrivent, il ne me rest plus qu’à supprimer cette ligne. Un avantage intéressant est que Visual Studio reconnaît déjà nullptr en tant que mot-clé et le met en évidence de manière appropriée.

J’utilise toujours:

  • NULL pour les pointeurs
  • '\0' pour les caractères
  • 0.0 pour les flotteurs et les doubles

où 0 ferait bien. C’est une question d’intention de signalisation. Cela dit, je ne suis pas anal à ce sujet.

  cerr << sizeof(0) << endl; cerr << sizeof(NULL) << endl; cerr << sizeof(void*) << endl; ============ On a 64-bit gcc RHEL platform you get: 4 8 8 ================ 

La morale de l'histoire. Vous devriez utiliser NULL lorsque vous traitez des pointeurs.

1) Il déclare votre intention (ne me faites pas chercher dans tout votre code en essayant de déterminer si une variable est un pointeur ou un type numérique).

2) Dans certains appels d'API qui attendent des arguments variables, ils utiliseront un pointeur NULL pour indiquer la fin de la liste d'arguments. Dans ce cas, utiliser un "0" au lieu de NULL peut causer des problèmes. Sur une plate-forme 64 bits, l'appel va_arg veut un pointeur 64 bits, mais vous ne transmettez qu'un entier 32 bits. Il me semble que vous comptez sur les autres 32 bits pour être mis à zéro pour vous? J'ai vu certains compilateurs (par exemple, icpc d'Intel) qui ne sont pas si gracieux - et cela a entraîné des erreurs d'exécution.

Si je me souviens bien, NULL est défini différemment dans les en-têtes que j’ai utilisés. Pour C, il est défini comme (void *) 0, et pour C ++, il est défini comme étant 0. Le code ressemblait à quelque chose comme:

 #ifndef __cplusplus #define NULL (void*)0 #else #define NULL 0 #endif 

Personnellement, j’utilise toujours la valeur NULL pour représenter les pointeurs nuls, cela explique explicitement que vous utilisez un pointeur plutôt qu’un type intégral. Oui en interne la valeur NULL est toujours 0 mais elle n’est pas représentée comme telle.

De plus, je ne compte pas sur la conversion automatique des entiers en valeurs booléennes, mais je les compare explicitement.

Par exemple, préférez utiliser:

 if (pointer_value != NULL || integer_value == 0) 

plutôt que:

 if (pointer_value || !integer_value) 

Autant dire que tout cela est corrigé en C ++ 11 où on peut simplement utiliser nullptr au lieu de NULL , et aussi nullptr_t qui est le type d’un nullptr .

Je dirais que l’histoire a parlé et ceux qui ont plaidé en faveur de l’utilisation de 0 (zéro) ont eu tort (y compris Bjarne Stroustrup). Les arguments en faveur de 0 étaient principalement esthétiques et “préférence personnelle”.

Après la création de C ++ 11, avec son nouveau type nullptr, certains compilateurs ont commencé à se plaindre (avec les parameters par défaut) du passage de 0 aux fonctions avec des arguments de pointeur, car 0 n’est pas un pointeur.

Si le code avait été écrit en utilisant NULL, une simple recherche et remplacement aurait pu être effectuée via le code pour le rendre nullptr. Si vous êtes bloqué avec du code écrit en utilisant le choix de 0 comme pointeur, il est beaucoup plus fastidieux de le mettre à jour.

Et si vous devez écrire un nouveau code maintenant sur le standard C ++ 03 (et ne pouvez pas utiliser nullptr), vous devriez vraiment utiliser NULL. Cela vous facilitera grandement la mise à jour dans le futur.

J’utilise habituellement 0. Je n’aime pas les macros, et rien ne garantit que certains en-têtes tiers que vous utilisez ne redéfinissent pas NULL comme quelque chose de bizarre.

Vous pourriez utiliser un object nullptr tel que proposé par Scott Meyers et d’autres jusqu’à ce que C ++ obtienne un mot clé nullptr:

 const // It is a const object... class nullptr_t { public: template operator T*() const // convertible to any type of null non-member pointer... { return 0; } template operator TC::*() const // or any type of null member pointer... { return 0; } private: void operator&() const; // Can't take address of nullptr } nullptr = {}; 

Google “nullptr” pour plus d’informations.

Une fois, j’ai travaillé sur une machine où 0 était une adresse valide et NULL était défini comme une valeur octale spéciale. Sur cette machine (0! = NULL), le code tel que

 char *p; ... if (p) { ... } 

ne fonctionnerait pas comme prévu. Tu devais écrire

 if (p != NULL) { ... } 

Bien que je pense que la plupart des compilateurs définissent NULL à 0, je me souviens encore de la leçon d’il ya quelques années: NULL n’est pas nécessairement 0.

Je pense que la norme garantit que NULL == 0, vous pouvez donc faire soit. Je préfère NULL car il documente votre intention.

Utiliser 0 ou NULL aura le même effet.

Cependant, cela ne signifie pas qu’ils sont tous deux de bonnes pratiques de programmation. Étant donné qu’il n’ya pas de différence de performances, le choix d’une option peu sensible à une alternative agnostique / abstraite est une mauvaise pratique de programmation. Aidez les lecteurs de votre code à comprendre votre processus de reflection .

NULL, 0, 0.0, ‘\ 0’, 0x00 et whatelse se traduisent tous par la même chose, mais sont des entités logiques différentes dans votre programme. Ils doivent être utilisés en tant que tels. NULL est un pointeur, 0 est la quantité, 0x0 est une valeur dont les bits sont intéressants, etc. Vous n’atsortingbuez pas “\ 0” à un pointeur, qu’il soit compilé ou non.

Je sais que certaines communautés encouragent la connaissance approfondie d’un environnement en rompant les contrats de l’environnement. Les programmeurs responsables, cependant, font du code maintenable et maintiennent ces pratiques hors de leur code.

Étrange, personne, y compris Stroustroup, n’a mentionné cela. En parlant beaucoup des normes et de l ‘esthétique, personne n’a remarqué qu’il est dangereux d’ utiliser 0 à la place de NULL , par exemple dans la liste des arguments variables de l ‘architecture où sizeof(int) != sizeof(void*) . Comme Stroustroup, je préfère 0 pour des raisons esthétiques, mais il faut faire attention à ne pas l’utiliser là où son type pourrait être ambigu.

J’essaie d’éviter toute la question en utilisant des références C ++ lorsque cela est possible. Plutôt que

 void foo(const Bar* pBar) { ... } 

vous pourriez souvent être capable d’écrire

 void foo(const Bar& bar) { ... } 

Bien sûr, cela ne marche pas toujours; mais les pointeurs nuls peuvent être surutilisés.

Je suis avec Stroustrup sur celui-ci 🙂 Puisque NULL ne fait pas partie du langage, je préfère utiliser 0.

Surtout des préférences personnelles, bien que l’on puisse argumenter que NULL rend tout à fait évident que l’object est un pointeur qui ne pointe actuellement vers rien, par exemple

 void *ptr = &something; /* lots o' code */ ptr = NULL; // more obvious that it's a pointer and not being used 

IIRC, le standard n’exige pas que NULL soit égal à 0. L’utilisation de ce qui est défini dans est probablement la meilleure pour votre compilateur.

Une autre facette de l’argument est de savoir si vous devez utiliser des comparaisons logiques (transtypage implicite sur bool) ou une vérification d’explicite par rapport à NULL, mais cela se résume également à la lisibilité.

Je préfère utiliser NULL car il est clair que votre intention est que la valeur représente un pointeur et non une valeur arithmétique. Le fait que ce soit une macro est malheureux, mais comme il est tellement enraciné, il y a peu de danger (à moins que quelqu’un fasse quelque chose de vraiment têtu). Je souhaite que ce soit un mot clé depuis le début, mais que pouvez-vous faire?

Cela dit, je n’ai aucun problème à utiliser des pointeurs comme valeurs de vérité en eux-mêmes. Tout comme avec NULL, c’est un idiome enraciné.

C ++ 09 appenda la construction nullptr qui, je pense, se fait attendre depuis longtemps.

J’utilise toujours 0. Pas pour de vraies raisons, juste parce que quand j’ai commencé à apprendre le C ++, j’ai lu quelque chose qui recommandait d’utiliser 0 et je l’ai juste fait comme ça. En théorie, il pourrait y avoir un problème de confusion en matière de lisibilité, mais dans la pratique, je n’ai jamais rencontré un tel problème en des milliers d’heures de travail et des millions de lignes de code. Comme le dit Stroustrup, ce n’est vraiment qu’un problème esthétique personnel jusqu’à ce que la norme devienne nulle.

Quelqu’un m’a dit une fois … Je vais redéfinir NULL à 69. Depuis, je ne l’utilise pas: P

Cela rend votre code très vulnérable.

Modifier:

Tout n’est pas parfait dans la norme. La macro NULL est une constante de pointeur nul C ++ définie par l’implémentation qui n’est pas entièrement compatible avec la macro C NULL, ce qui, outre le masquage de type implicite, le convertit en un outil inutile et sujet aux erreurs.

NULL ne se comporte pas comme un pointeur nul mais comme un littéral O / OL.

Dis-moi que le prochain exemple n’est pas déroutant:

 void foo(char *); void foo(int); foo(NULL); // calls int version instead of pointer version! 

Est-ce à cause de tout cela, dans la nouvelle norme apparaît std :: nullptr_t

Si vous ne voulez pas attendre le nouveau standard et que vous voulez utiliser un nullptr, utilisez au moins un décent comme celui proposé par Meyers (voir le commentaire de jon.h).

Eh bien, je plaide pour ne pas utiliser 0 ou NULL pointeurs lorsque cela est possible.

Leur utilisation entraînera tôt ou tard des erreurs de segmentation dans votre code. Dans mon expérience, les pointeurs de gereral sont l’une des plus grandes sources de bogues en C ++.

De plus, cela conduit à des instructions “if-not-null” sur tout votre code. Beaucoup plus agréable si vous pouvez toujours compter sur un état valide.

Il y a presque toujours une meilleure alternative.

Définir un pointeur sur 0 n’est pas si clair. Surtout si vous venez dans une langue autre que C ++. Cela comprend C ainsi que Javascript.

J’ai récemment volé avec un code comme celui-ci:

virtual void DrawTo(BITMAP *buffer) =0;

pour la fonction virtuelle pure pour la première fois. Je pensais que c’était du jiberjash magique pendant une semaine. Quand je me suis rendu compte que c’était juste de définir le pointeur de fonction sur un null (comme les fonctions virtuelles ne sont que des pointeurs de fonctions dans la plupart des cas pour C ++), je me suis lancé.

virtual void DrawTo(BITMAP *buffer) =null;

aurait été moins déroutant que cette basterdation sans espacement approprié pour mes nouveaux yeux. En fait, je me demande pourquoi C ++ n’utilise pas de minuscules null tout comme il utilise des minuscules fausses et vraies maintenant.