N'utilisez pas NULL en C++

Préférez 0 ou nullptr

N’utilisez pas NULL en C++

Je vois très souvent des gens utiliser “NULL” en C++. “NULL” ne devrait pas être employé. Nous allons voir pourquoi, mais revenons d’abord sur son apparition dans le langage C.

Une histoire NULL

Au tout début du C, les constantes n’existaient pas. Dès le moment où l’on avait besoin de valeurs constantes, on utilisait le préprocesseur. Le type le plus abstrait à cette lointaine époque, était “char*”. Donc la macro “NULL” qui était définit dans “stdio.h”, équivalait à:

#define NULL ((char*)0)

Le C était à l’origine un langage basé sur les fonctions. Tout prenait des arguments, et tout en retournait. Ce n’est qu’un peu plus tard que le concept de procédure vit le jour. Il fallait donc représenter cette notion, et c’est comme cela que le mot clé void, a été introduit. Le type le plus abstrait était donc devenu le “void*”. La macro “NULL” a donc changé:

#define NULL ((void*)0)

Le problème avec le C++

Le problème ne se pose qu’en C++. En effet, ce langage n’est pas du C, c’est un langage à part entière, qui est rétro-compatible avec le C. Ce n’est en rien un C avec des classes. Or en C++, la notion de POO a impliqué un support plus strict des types. Certaines permissivités du C n’étaient plus possible. En effet, utiliser NULL sous sa forme void* pose problème puisqu’il faut explicitement réaliser une conversion de type.

#define NULL ((void*)0)
void* ptrVoid = NULL; // Valide
int* ptrInt = NULL; // Invalide
int* ptrIntCast = (int*) NULL; // Valide

Pourquoi ça fonctionne en C++

évidemment, certains me rétorqueront, que ça fonctionne en C++. C’est tout simplement qu’en C++, NULL n’est pas défini de la même manière. Ouvrons le fichier “stddef.h” (utilisé par “stdlib.h”), fournit avec le langage C, et recherchons la définition de NULL:

#ifndef __cplusplus
#define NULL ((void *)0)
#else   /* C++ */
#define NULL 0

Ici, on voit bien que NULL est en fait 0, dans le cas du C++.

Le danger d’utiliser NULL

C’est très simple, en utilisant NULL au lieu de 0, vous laissez sous-entendre que NULL serait de type pointeur. C’est faux, NULL vaut 0, l’entier 0 ! On arrive donc dans des cas de figures où la détection de bug devient difficile.

Exemple:

void fonction(int entier);
void fonction(int* pointeur);

fonction(NULL);

Ici contrairement à ce que l’on pourrait penser, fonction(NULL) correspond à la fonction “void fonction(int entier);” et non “void fonction(int* pointeur);” ! Si on avait mis “fonction(0)”, on s’en serait aperçu immédiatement.

Utilisez 0 !

NULL ne fait pas parti du C++ ! Il est juste là pour assurer une rétro-compabilité avec le C. De plus, en utilisant 0, vous n’utilisez pas de macros (macros qui sont à éviter en C++, à cause de leurs effets secondaires), et surtout, vous n’avez pas besoin d’inclure les entêtes “stdlib.h” ou “stddef.h”, qui sont normalement réservés au langage C.

Qui le recommande ?

Bien évidemment, je ne suis pas un gus isolé qui vous propose cela sur un coup de tête. L’utilisation du 0 en lieu et place du NULL est recommandée par Bjarne Stroustrup, le créateur du C++ lui-même ! (Voir: http://www.stroustrup.com/bs_faq2.html#null)

Cas du C++ moderne (C++0x ou C++11)

Dans la nouvelle version du C++ (C++11), ce problème a enfin été traité. Un nouveau mot clé a fait son apparition: nullptr. Le typage de celui-ci est correctement appliqué.

Exemple:

void fonction(int entier);
void fonction(int* pointeur);

fonction(nullptr);

Dans cet exemple, la fonction appelée est bien “void fonction(int* pointeur);”. Si vous possédez un compilateur gérant la nouvelle version du C++, je vous invite à l’utiliser dès que possible.