Que signifie faire une “ vérification nulle ” en C ou C ++?

Jai appris le C ++ et jai du mal à comprendre null. En particulier, les didacticiels que jai lus mentionnent une « vérification de null », mais je ne suis pas sûr de ce que cela signifie ou pourquoi cest nécessaire.

  • Quest-ce qui est exactement nul?
  • Que signifie « vérifier la valeur nulle »?
  • Dois-je toujours vérifier la valeur nulle?

Tout exemple de code serait très apprécié.

  • / p>

    Commentaires

  • Réponse

    En C et C ++, les pointeurs sont intrinsèquement dangereux, cest-à-dire que lorsque vous déréférencer un pointeur, il est de votre responsabilité de vous assurer quil pointe vers un endroit valide; cela fait partie de ce quest la « gestion manuelle de la mémoire » (par opposition aux schémas de gestion automatique de la mémoire implémentés dans des langages comme Java , PHP ou le runtime .NET, qui ne vous permettra pas de créer des références invalides sans effort considérable).

    Une solution courante qui détecte de nombreuses erreurs consiste à définir tous les pointeurs qui ne pointent vers rien comme NULL (ou, en C ++ correct, 0), et vérifiez cela avant daccéder au pointeur. Plus précisément, il est courant dinitialiser tous les pointeurs sur NULL (sauf si vous avez déjà quelque chose sur lequel les pointer lorsque vous les déclarez), et de les définir sur NULL lorsque vous delete ou free() eux (à moins quils ne sortent de la portée immédiatement après cela). Exemple (en C, mais aussi en C ++ valide):

    void fill_foo(int* foo) { *foo = 23; // this will crash and burn if foo is NULL } 

    Une meilleure version:

    void fill_foo(int* foo) { if (!foo) { // this is the NULL check printf("This is wrong\n"); return; } *foo = 23; } 

    Sans la vérification de null, passer un pointeur NULL dans cette fonction provoquera un segfault, et vous ne pouvez rien faire – le système dexploitation va simplement tuer votre processus et peut-être faire un core-dump ou faire apparaître une boîte de dialogue de rapport de plantage. Avec la vérification des valeurs nulles en place, vous pouvez effectuer une gestion correcte des erreurs et récupérer correctement – corrigez le problème vous-même, abandonnez lopération en cours, écrivez une entrée de journal, notifiez lutilisateur, tout ce qui est approprié.

    Commentaires

    • @MrLister que voulez-vous dire, les vérifications nulles ne fonctionnent pas ‘ en C ++? Il vous suffit dinitialiser le pointeur sur null lorsque vous le déclarez.
    • Ce que je veux dire, cest que vous devez vous rappeler de définir le pointeur sur NULL ou il a gagné ‘ t travailler. Et si vous vous souvenez, en dautres termes, si vous savez que le pointeur est NULL, vous naurez pas ‘ besoin dappeler fill_foo de toute façon. fill_foo vérifie si le pointeur a une valeur, pas si le pointeur a une valeur valide . En C ++, il nest pas garanti que les pointeurs soient NULL ou quils aient une valeur valide.
    • Un assert () serait une meilleure solution ici. Il ny a ‘ aucune raison dessayer de  » être en sécurité « . Si NULL a été passé, cela ‘ est manifestement faux, alors pourquoi ne pas simplement planter explicitement pour rendre le programmeur pleinement conscient? (Et en production, cela na ‘ pas dimportance, car vous ‘ avez prouvé que personne nappellera fill_foo () avec NULL, nest-ce pas? Vraiment, ‘ nest pas si difficile.)
    • Ne ‘ oublier pour mentionner quune version encore meilleure de cette fonction devrait utiliser des références au lieu de pointeurs, ce qui rendra la vérification NULL obsolète.
    • Ce nest pas le but de la gestion manuelle de la mémoire, et un programme géré explosera aussi, ( ou lever une exception au moins, tout comme un programme natif le fera dans la plupart des langues,) si vous essayez de déréférencer une référence nulle.

    Réponse

    Les autres réponses couvraient à peu près votre question exacte. Une vérification nulle est effectuée pour être sûr que le pointeur que vous avez reçu pointe réellement vers une instance valide dun type (objets, primitives, etc.).

    Je vais ajouter mon propre conseil ici, Évitez les vérifications nulles. 🙂 Les vérifications nulles (et dautres formes de programmation défensive) encombrent le code et le rendent en fait plus sujet aux erreurs que les autres techniques de gestion des erreurs.

    Ma technique préférée en ce qui concerne pointeurs dobjet consiste à utiliser le modèle dobjet nul . Cela signifie renvoyer un (pointeur – ou mieux encore, une référence à un) tableau ou une liste vide au lieu de null, ou renvoyer une chaîne vide («  ») au lieu de null, ou même la chaîne « 0 » (ou quelque chose déquivalent à « rien » dans le contexte) où vous vous attendez à ce quelle soit analysée en un entier.

    En bonus, voici « un petit quelque chose que vous ne saviez peut-être pas sur le pointeur nul, qui a été (formellement) implémenté pour la première fois par CAR Hoare pour le langage Algol W en 1965.

    Je lappelle mon erreur dun milliard de dollars. Cétait linvention de la référence nulle en 1965. À cette époque, je concevais le premier système de type complet pour les références dans un objet orienté langage (ALGOL W). Mon objectif était de massurer que toute utilisation de références soit absolument sûre, avec une vérification effectuée automatiquement par le compilateur. Mais je nai pas pu résister à la tentation de mettre une référence nulle, simplement parce que cétait le cas facile à mettre en œuvre. Cela a conduit à dinnombrables erreurs, vulnérabilités et plantages système, qui ont probablement causé un milliard de dollars de souffrances et de dommages au cours des quarante dernières années.

    Commentaires

    • Null Object est encore pire que davoir simplement un pointeur nul. Si un algorithme X nécessite des données Y que vous navez pas, alors cest un bogue dans votre programme , que vous cachez simplement en prétendant que vous le faites.
    • Cela dépend de le contexte, et dans les deux cas, le test de la  » présence de données  » bat le test de null dans mon livre. Daprès mon expérience, si un algorithme fonctionne, par exemple, sur une liste et que la liste est vide, alors lalgorithme na tout simplement rien à faire, et il accomplit cela en utilisant simplement des instructions de contrôle standard telles que for / foreach.
    • Si lalgorithme na rien à voir, alors pourquoi lappelez-vous? Et la raison pour laquelle vous avez peut-être voulu lappeler en premier lieu est parce que cela fait quelque chose dimportant .
    • @DeadMG Parce que les programmes concernent la saisie, et dans le monde réel, contrairement devoirs, les entrées peuvent être sans importance (par exemple vide). Le code est toujours appelé de toute façon. Vous avez deux options: soit vous vérifiez la pertinence (ou le vide), soit vous concevez vos algorithmes pour quils lisent et fonctionnent bien sans vérifier explicitement la pertinence à laide dinstructions conditionnelles.
    • Je suis venu ici pour faire presque le même commentaire, alors je vous ai donné mon vote à la place. Cependant, jajouterais également que cela est représentatif dun problème plus important des objets zombies – à chaque fois que vous avez des objets avec une initialisation (ou destruction) en plusieurs étapes qui ne sont pas complètement vivants mais pas tout à fait morts. Lorsque vous voyez du code  » sûr  » dans des langues sans finalisation déterministe qui a ajouté des vérifications dans chaque fonction pour voir si lobjet a été supprimé, cest ce problème général délever la tête de ‘. Vous ne devriez jamais if-null, vous devriez travailler avec des états qui ont les objets dont ils ont besoin pour leur durée de vie.

    Réponse

    La valeur du pointeur nul représente un « nulle part » bien défini; cest une valeur de pointeur invalide dont la comparaison est garantie de manière inégale à toute autre valeur de pointeur. Tenter de déréférencer un pointeur nul entraîne un comportement indéfini et entraînera généralement une erreur dexécution, vous voulez donc vous assurer quun pointeur nest pas NULL avant dessayer de le déréférencer. Un certain nombre de fonctions de bibliothèque C et C ++ renverront un pointeur nul pour indiquer une condition derreur. Par exemple, la fonction de bibliothèque malloc renverra une valeur de pointeur nulle si elle ne peut pas allouer le nombre doctets qui ont été demandés, et tenter daccéder à la mémoire via ce pointeur entraînera (généralement) à une erreur dexécution:

    int *p = malloc(sizeof *p * N); p[0] = ...; // this will (usually) blow up if malloc returned NULL 

    Nous devons donc nous assurer que lappel malloc a réussi en vérifiant la valeur de p contre NULL:

    int *p = malloc(sizeof *p * N); if (p != NULL) // or just if (p) p[0] = ...; 

    Maintenant, accrochez-vous à vos chaussettes une minute, cela va avoir un bit bosselé.

    Il existe un pointeur nul valeur et un pointeur nul constant , et les deux ne sont pas nécessairement identiques. La valeur du pointeur nul est la valeur que larchitecture sous-jacente utilise pour représenter « nulle part ». Cette valeur peut être 0x00000000, 0xFFFFFFFF, 0xDEADBEEF ou quelque chose de complètement différent. Ne supposez pas que la valeur du pointeur nul est toujours 0.

    La constante du pointeur nul, OTOH, est toujours une expression intégrale de valeur 0. En ce qui concerne votre code source , 0 (ou toute expression intégrale évaluée à 0) représente un pointeur nul. C et C ++ définissent la macro NULL comme la constante de pointeur null. Lorsque votre code est compilé, le pointeur nul constant sera remplacé par le pointeur nul valeur approprié dans le code machine généré.

    De plus, sachez que NULL nest quune des nombreuses valeurs de pointeur invalides possibles; si vous déclarez une variable de pointeur automatique sans linitialiser explicitement, comme

    int *p; 

    la valeur initialement stockée dans la variable est indéterminée , et peut ne pas correspondre à une adresse mémoire valide ou accessible. Malheureusement, il ny a pas de moyen (portable) de savoir si une valeur de pointeur non NULL est valide ou non avant de tenter de l utiliser. Donc, si vous avez affaire à des pointeurs, il est généralement judicieux de les initialiser explicitement à NULL quand vous les déclarez, et pour les mettre à NULL quand ils ne pointent pas activement vers quoi que ce soit.

    Notez que cest plus un problème en C quen C ++; Le C ++ idiomatique ne devrait pas trop utiliser de pointeurs.

    Réponse

    Il y a quelques méthodes, toutes font essentiellement la même chose chose.

     int *foo = NULL; //sometimes set to 0x00 or 0 or 0L instead of NULL 

    null check (vérifier si le pointeur est nul), version A

     if( foo == NULL) 

    contrôle nul, version B

     if( !foo ) //since NULL is defined as 0, !foo will return a value from a null pointer 

    contrôle nul, version C

     if( foo == 0 ) 

    Des trois, je préfère utiliser la première vérification car elle indique explicitement aux futurs développeurs ce que vous essayiez de vérifier ET cela indique clairement que vous vous attendiez à ce que foo soit un pointeur.

    Réponse

    Vous ne le faites pas. La seule raison dutiliser un pointeur en C ++ est que vous souhaitez explicitement la présence de pointeurs nuls; sinon, vous pouvez prendre une référence, qui est à la fois sémantiquement plus facile à utiliser et garantit non nulle.

    Commentaires

    • @James: ‘ nouveau ‘ en mode noyau?
    • @James: Une implémentation de C ++ qui représente les capacités quune grande majorité de C ++ les codeurs apprécient. Cela inclut toutes les fonctionnalités du langage C ++ 03 (sauf export) et toutes les fonctionnalités de la bibliothèque C ++ 03 et TR1 et une bonne partie de C ++ 11.
    • Je souhaite que les gens ne ‘ t disent que  » les références garantissent non nulles.  » Elles ne ‘ t. Il est aussi facile de générer une référence nulle quun pointeur nul, et ils se propagent de la même manière.
    • @Stargazer: La question est 100% redondante lorsque vous utilisez simplement les outils comme les concepteurs du langage et bien la pratique vous suggère de le faire.
    • @DeadMG, il n ‘ pas si elle est redondante. Vous navez ‘ pas répondu à la question . Je ‘ je le répète: -1.

    Réponse

    Si vous ne cochez pas la valeur NULL, en particulier, sil sagit dun pointeur vers une structure, vous avez peut-être rencontré une vulnérabilité de sécurité – la déréférence de pointeur NULL. La déréférence de pointeur NULL peut entraîner dautres vulnérabilités de sécurité graves telles que le dépassement de tampon, condition de concurrence … qui peut permettre à un attaquant de prendre le contrôle de votre ordinateur.

    De nombreux éditeurs de logiciels comme Microsoft, Oracle, Adobe, Apple … publient un correctif logiciel pour corriger ces vulnérabilités de sécurité. Je pense que vous devriez vérifier la valeur NULL de chaque pointeur 🙂

    Laisser un commentaire

    Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *