web-dev-qa-db-fra.com

Pourquoi les pointeurs NULL sont-ils définis différemment en C et C ++?

En C, NULL est défini comme (void *)0 alors qu'en C++ c'est 0. Pourquoi en est-il ainsi? En C, je peux comprendre que si NULL n'est pas transtypé en (void *) alors les compilateurs peuvent/peuvent ne pas générer d'avertissement. À part cela, y a-t-il une raison?

70
mohit

De retour en C++ 03, un pointeur nul a été défini par la spécification ISO (§4.10/1) comme

Une constante de pointeur nul est une expression constante intégrale (5.19) rvalue de type entier évaluée à zéro.

C'est pourquoi en C++ vous pouvez écrire

int* ptr = 0;

En C, cette règle est similaire, mais un peu différente (§6.3.2.3/3):

Une expression de constante entière avec la valeur 0, ou une telle expression transtypée en type void *, Est appelée une constante de pointeur nulle.55) Si une constante de pointeur nulle est convertie en un type de pointeur, le pointeur résultant, appelé pointeur nul, est garanti de comparer inégale à un pointeur à tout objet ou fonction.

Par conséquent, les deux

int* ptr = 0;

et

int* ptr = (void *)0

sont légales. Cependant, je suppose que le casting void* Est là pour que des instructions comme

int x = NULL;

produire un avertissement du compilateur sur la plupart des systèmes. En C++, cela ne serait pas légal car vous ne pouvez pas implicitement convertir un void* En un autre type de pointeur implicitement sans transtypage. Par exemple, c'est illégal:

int* ptr = (void*)0; // Legal C, illegal C++

Cependant, cela conduit à des problèmes car le code

int x = NULL;

est légal C++. À cause de cela et de la confusion qui en résulte (et d'un autre cas, présenté plus loin), depuis C++ 11, il existe un mot clé nullptr représentant un pointeur nul:

int* ptr = nullptr;

Cela n'a aucun des problèmes ci-dessus.

L'autre avantage de nullptr sur 0 est qu'il joue mieux avec le système de type C++. Par exemple, supposons que j'ai ces deux fonctions:

void DoSomething(int x);
void DoSomething(char* x);

Si j'appelle

DoSomething(NULL);

C'est équivalent à

DoSomething(0);

qui appelle DoSomething(int) au lieu de la DoSomething(char*) attendue. Cependant, avec nullptr, je pouvais écrire

DoSomething(nullptr);

Et il appellera la fonction DoSomething(char*) comme prévu.

De même, supposons que j'ai un vector<Object*> Et que je souhaite définir chaque élément comme un pointeur nul. En utilisant l'algorithme std::fill, Je pourrais essayer d'écrire

std::fill(v.begin(), v.end(), NULL);

Cependant, cela ne se compile pas, car le système de modèles traite NULL comme un int et non comme un pointeur. Pour résoudre ce problème, je devrais écrire

std::fill(v.begin(), v.end(), (Object*)NULL);

C'est moche et va quelque peu à l'encontre de l'objectif du système de modèles. Pour résoudre ce problème, je peux utiliser nullptr:

std::fill(v.begin(), v.end(), nullptr);

Et puisque nullptr est connu pour avoir un type correspondant à un pointeur nul (spécifiquement, std::nullptr_t), Cela se compilera correctement.

J'espère que cela t'aides!

101
templatetypedef

En C, NULL se développe en une "constante de pointeur nul" définie par l'implémentation. Une constante de pointeur nul est soit une expression de constante entière avec la valeur 0, soit une telle expression transtypée en void*. Une implémentation C peut donc définir NULL soit 0 ou comme ((void*)0).

En C++, les règles pour les constantes de pointeur nul sont différentes. En particulier, ((void*)0) n'est pas une constante de pointeur null C++, donc une implémentation C++ ne peut pas définir NULL de cette façon.

16
Keith Thompson