Vérification du pointeur NULL en C / C++ [fermé]
Dans une révision récente du code, un contributeur essaie de faire en sorte que toutes les vérifications NULL
sur les pointeurs soient effectuées de la manière suivante:
int * some_ptr;
// ...
if (some_ptr == NULL)
{
// Handle null-pointer error
}
else
{
// Proceed
}
Au Lieu de
int * some_ptr;
// ...
if (some_ptr)
{
// Proceed
}
else
{
// Handle null-pointer error
}
Je suis d'accord que sa façon est un peu plus claire dans le sens où elle dit explicitement "assurez-vous que ce pointeur n'est pas NULL", mais je contreerais cela en disant que quiconque travaille sur ce code comprendrait que l'utilisation d'une variable pointeur dans une instruction if
vérifie implicitement NULL
. Aussi, Je sentez que la deuxième méthode a moins de chance d'introduire un bug de l'acabit:
if (some_ptr = NULL)
Ce qui est juste une douleur absolue à trouver et à déboguer.
De quelle façon préférez-vous et pourquoi?
15 réponses
Dans mon expérience, les tests de la forme if (ptr)
ou if (!ptr)
sont privilégiées. Ils ne dépendent pas de la définition du symbole NULL
. Ils n'exposent pas la possibilité de l'affectation accidentelle. Et ils sont clairs et succincts.
Edit: comme le souligne SoapBox dans un commentaire, ils sont compatibles avec les classes C++ telles que auto_ptr
qui sont des objets qui agissent comme des pointeurs et qui fournissent une conversion en bool
pour activer exactement cet idiome. Pour ces objets, explicite la comparaison avec NULL
devrait invoquer une conversion en pointeur qui peut avoir d'autres effets secondaires sémantiques ou être plus coûteuse que la simple vérification d'existence que la conversion bool
implique.
J'ai une préférence pour le code qui dit ce que cela signifie sans texte inutile. {[7] } a la même signification que if (ptr)
mais au prix d'une spécificité redondante. La prochaine chose logique est d'écrire if ((ptr != NULL) == TRUE)
et de cette façon se trouve la folie. Le langage C est clair qu'un booléen testé par if
, while
ou Le like a une signification spécifique de valeur non nulle est vrai et Zéro est faux. La redondance ne le rend pas plus clair.
Je vais commencer par ceci: la cohérence est Roi, la décision est moins importante que la cohérence dans votre base de code.
En C++
NULL est défini comme 0 ou 0L en C++.
Si vous avez lu le langage de programmation C++ Bjarne Stroustrup suggère d'utiliser 0
explicitement pour éviter la macro NULL
lors de l'affectation, Je ne suis pas sûr qu'il ait fait la même chose avec les comparaisons, Cela fait un moment que j'ai lu le livre, je pense qu'il a juste fait if(some_ptr)
sans une comparaison explicite mais je suis floue à ce sujet.
La raison en est que la macro NULL
est trompeuse (comme presque toutes les macros le sont) c'est en fait 0
littéral, pas un type unique comme son nom l'indique. Éviter les macros est l'une des directives générales en C++. D'un autre côté, 0
ressemble à un entier et il n'est pas comparé ou affecté à des pointeurs. Personnellement, je pourrais aller de toute façon, mais généralement je saute la comparaison explicite (bien que certaines personnes n'aiment pas c'est probablement pourquoi vous avez un contributeur suggérant un changement de toute façon).
Indépendamment des sentiments personnels, c'est en grande partie un choix de moindre mal car il n'y a pas une bonne méthode.
C'est clair et un idiome commun et je le préfère, il n'y a aucune chance d'attribuer accidentellement une valeur pendant la comparaison et il se lit clairement:
if(some_ptr){;}
Ceci est clair si vous savez que some_ptr
est un type de pointeur, mais il peut aussi ressembler à une comparaison d'entiers:
if(some_ptr != 0){;}
Ceci est clair, dans les cas courants, cela a du sens... Mais c'est une abstraction qui fuit, NULL
est en fait 0
littéral et pourrait finir par être mal utilisé facilement:
if(some_ptr != NULL){;}
C++0x a nullptr qui est maintenant la méthode préférée car elle est explicite et précise, faites juste attention à l'affectation accidentelle:
if(some_ptr != nullptr){;}
Jusqu'à ce que vous soyez capable de migrer vers C++0x, je dirais que c'est une perte de temps à vous soucier de laquelle de ces méthodes vous utilisez, elles sont toutes insuffisantes c'est pourquoi nullptr a été inventé (avec des problèmes de programmation génériques qui ont abouti à une transmission parfaite.) La chose la plus importante est de maintenir la cohérence.
En C
C est une bête différente.
En C NULL peut être défini comme 0 ou comme ((void *)0), C99 permet l'implémentation de constantes de pointeur null définies. Donc, il s'agit en fait de la définition de NULL de L'implémentation et vous devrez l'inspecter dans votre bibliothèque standard.
Les Macros sont très commun et en général, ils sont beaucoup utilisés pour compenser les lacunes dans le support de programmation générique dans la langue et d'autres choses aussi. Le langage est beaucoup plus simple et la dépendance au pré-processeur plus commun.
De ce point de vue, je recommanderais probablement d'utiliser la définition de macro NULL
dans C.
J'utilise if (ptr)
, mais cela ne vaut absolument pas la peine d'être discuté.
J'aime mon chemin parce qu'il est concis, bien que d'autres disent que == NULL
le rend plus facile à lire et plus explicite. Je vois d'où ils viennent, Je ne suis pas d'accord avec les choses supplémentaires qui facilitent les choses. (Je déteste la macro, donc je suis biaisé.) Jusqu'à vous.
Je ne suis pas d'accord avec votre argument. Si vous ne recevez pas d'avertissements pour les affectations dans un conditionnel, vous devez augmenter vos niveaux d'avertissement. Simple que cela. (Et pour l'amour de tout ce qui est bon, ne changez pas de autour de.)
Note En C++0x, nous pouvons faire if (ptr == nullptr)
, qui pour moi fait lire plus agréable. (Encore une fois, je déteste la macro. Mais {[3] } est sympa.) Je fais toujours if (ptr)
, cependant, juste parce que c'est ce à quoi je suis habitué.
Franchement, je ne vois pas pourquoi ça compte. L'un ou l'autre est assez clair et toute personne modérément expérimentée avec C ou c++ devrait comprendre les deux. Un commentaire, cependant:
Si vous prévoyez de reconnaître l'erreur et de ne pas continuer à exécuter la fonction (c'est-à-dire que vous allez lancer une exception ou renvoyer immédiatement un code d'erreur), vous devriez en faire une clause guard:
int f(void* p)
{
if (!p) { return -1; }
// p is not null
return 0;
}
De cette façon, vous évitez le code "Flèche"."
En fait, j'utilise les deux variantes.
Il y a des situations, où vous vérifiez d'abord la validité d'un pointeur, et s'il est nul, vous retournez/sortez d'une fonction. (Je sais que cela peut conduire à la discussion "si une fonction n'a qu'un seul point de sortie")
La plupart du temps, vous vérifiez le pointeur, puis faites ce que vous voulez, puis résolvez le cas d'erreur. Le résultat peut être le code en retrait x-times laid avec plusieurs if.
Personnellement, j'ai toujours utilisé if (ptr == NULL)
parce que cela rend mon intention explicite, mais à ce stade, c'est juste une habitude.
L'utilisation de =
à la place de {[2] } sera interceptée par tout compilateur compétent avec les paramètres d'avertissement corrects.
, Le point important est de choisir un style cohérent pour votre groupe et de s'y tenir. Peu importe la façon dont vous allez, vous finirez par vous y habituer, et la perte de friction lorsque vous travaillez dans le code d'autres personnes sera la bienvenue.
Juste un point de plus en faveur de la foo == NULL
pratique:
Si foo
est, disons, un int *
ou un bool *
, alors la vérification if (foo)
peut accidentellement être interprétée par un lecteur comme testant la valeur du pointee, c'est-à-dire comme if (*foo)
. La comparaison NULL
ici est un rappel que nous parlons d'un pointeur.
Mais je suppose qu'une bonne convention de nommage rend cet argument Sans objet.
le langage de programmation C (K & R) vous demanderait de vérifier null = = ptr pour éviter une affectation accidentelle.
Si le style et le format font partie de vos commentaires, il devrait y avoir un guide de style convenu à mesurer. S'il y en a un, faites ce que dit le guide de style. S'il n'y en a pas, des détails comme celui-ci devraient être laissés tels qu'ils sont écrits. C'est une perte de temps et d'énergie, et distrait de ce que les critiques de code devraient vraiment découvrir. Sérieusement, sans guide de style, je pousserais à ne pas changer de code comme celui-ci par principe, même s'il n'utilise pas la convention I préférer.
Et ce n'est pas important, mais ma préférence personnelle est if (ptr)
. Le sens est plus immédiatement évident pour moi que même if (ptr == NULL)
.
Peut-être qu'il essaie de dire qu'il vaut mieux gérer les conditions d'erreur avant le chemin heureux? Dans ce cas, je ne suis toujours pas d'accord avec l'examinateur. Je ne sais pas qu'il existe une convention acceptée pour cela, mais à mon avis, la condition la plus "normale" devrait venir en premier dans toute déclaration if. De cette façon j'ai moins de recherches à faire pour comprendre ce que la fonction est tout au sujet et comment il fonctionne.
L'exception à ceci est si l'erreur me fait fuir la fonction, ou je peux en récupérer avant de passer à autre chose. Dans ces cas, je gère d'abord l'erreur:
if (error_condition)
bail_or_fix();
return if not fixed;
// If I'm still here, I'm on the happy path
En traitant la condition inhabituelle à l'avance, je peux m'en occuper et ensuite l'oublier. Mais si Je ne peux pas revenir sur le chemin heureux en le manipulant à l'avant, alors il devrait être géré après le cas principal parce que cela rend le code plus compréhensible. Dans mon opinion.
Mais si ce n'est pas dans un guide de style alors c'est juste mon opinion, et votre opinion est tout aussi valable. Ne laissez pas un critique pseudo-standardiser juste parce qu'il a une opinion.
Je pense que c'est assez clair:
if( !some_ptr )
{
// handle null-pointer error
}
else
{
// proceed
}
Lisez-le comme " S'il n'y a pas de pointeur ..."
En outre, il est concis et ne présente aucun danger d'affectations accidentelles.
C'est l'un des principes fondamentaux des deux langages que les pointeurs évaluent à un type et une valeur qui peuvent être utilisés comme expression de contrôle, bool
en C++ et int
en C. Il suffit de l'utiliser.
Je suis un grand fan du fait que C / C++ ne vérifie pas les types dans les conditions booléennes dans if
, for
et while
déclarations. J'utilise toujours ce qui suit:
if (ptr)
if (!ptr)
Même sur des entiers ou un autre type qui se convertit en bool:
while(i--)
{
// Something to do i times
}
while(cin >> a >> b)
{
// Do something while you've input
}
Le codage dans ce style est plus lisible et plus clair pour moi. Juste mon opinion personnelle.
Récemment, tout en travaillant sur le microcontrôleur OKI 431, j'ai remarqué que ce qui suit:
unsigned char chx;
if (chx) // ...
Est plus efficace que
if (chx == 1) // ...
Parce que dans cas, le compilateur doit comparer la valeur de chx à 1. Où chx est juste un vrai/faux drapeau.
La plupart des compilateurs que j'ai utilisés avertiront au moins sur l'affectation if
sans sucre de syntaxe supplémentaire, donc je n'achète pas cet argument. Cela dit, j'ai utilisé à la fois professionnellement et n'ai aucune préférence pour l'un ou l'autre. Le == NULL
est certainement plus clair à mon avis.
- Les pointeurs ne sont pas des booléens
- les compilateurs C/C++ modernes émettent un avertissement lorsque vous écrivez
if (foo = bar)
par accident.
Par conséquent, je préfère
if (foo == NULL)
{
// null case
}
else
{
// non null case
}
Ou
if (foo != NULL)
{
// non null case
}
else
{
// null case
}
Cependant, si j'écrivais un ensemble de directives de style, Je ne mettrais pas des choses comme ça dedans, je mettrais des choses comme:
Assurez-vous de faire une vérification nulle sur le pointeur.