web-dev-qa-db-fra.com

Vérification du pointeur NULL en C / C ++

Dans une récente révision de code, un contributeur tente de faire en sorte que toutes les vérifications de 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 conviens que sa façon de faire est un peu plus claire dans le sens où il dit explicitement "Assurez-vous que ce pointeur n'est pas NULL", mais je contredirais cela en disant que toute personne travaillant sur ce code comprendrait que l'utilisation d'une variable de pointeur dans L'instruction if recherche implicitement NULL. De plus, je pense que la deuxième méthode a moins de chance d'introduire un bogue du genre:

if (some_ptr = NULL)

ce qui est juste une douleur absolue à trouver et à déboguer.

De quel côté préférez-vous et pourquoi?

140
Bryan Marble

D'après mon expérience, les tests de la forme if (ptr) ou if (!ptr) sont préférés. Ils ne dépendent pas de la définition du symbole NULL. Ils n'exposent pas la possibilité d'une affectation accidentelle. Et ils sont clairs et succincts.

Edit: Comme le souligne SoapBox dans un commentaire, elles 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, une comparaison explicite avec NULL devrait invoquer une conversion en pointeur qui peut avoir d'autres effets secondaires sémantiques ou être plus onéreuse que le simple contrôle 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. if (ptr != NULL) 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 c’est ainsi que réside la folie. Le langage C indique clairement qu'un booléen testé par if, while ou similaire a une signification spécifique: la valeur autre que zéro est vraie et zéro, fausse. La redondance ne le rend pas plus clair.

186
RBerteig

if (foo) est assez clair. Utilise le.

50
wilx

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 par 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 quand je fais une mission , je ne suis pas sûr s'il a fait la même chose avec les comparaisons, cela fait longtemps que je n'ai pas lu le livre, je pense qu'il vient de faire if(some_ptr) sans comparaison explicite, mais je suis flou sur ce point. .

La raison en est que la macro NULL est trompeuse (comme presque toutes les macros), il s'agit en fait de 0 littéral, et non d'un type unique, comme son nom l'indique. Éviter les macros est l’une des instructions générales en C++. D'autre part, 0 ressemble à un entier et il ne l'est pas lorsqu'il est comparé ou attribué à des pointeurs. Personnellement, je pourrais aller dans les deux sens, mais en général, je saute la comparaison explicite (bien que certaines personnes n’aiment pas cela, ce qui explique probablement pourquoi vous avez un contributeur proposant de toute façon un changement).

Indépendamment des sentiments personnels, il s’agit en grande partie du choix du moins pervers car il n’existe pas une seule méthode.

C’est clair et un langage courant, et je le préfère, il n’ya aucune chance d’attribuer accidentellement une valeur lors de la comparaison et il est clairement indiqué:

if(some_ptr){;}

Ceci est clair si vous savez que some_ptr est un type de pointeur, mais cela peut également ressembler à une comparaison d'entiers:

if(some_ptr != 0){;}

C’est clair, dans les cas courants, c’est logique ... Mais c’est une abstraction qui fuit, NULL est en fait 0 au sens littéral et pourrait finir par être mal utilisé:

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 attention aux assignations accidentelles:

if(some_ptr != nullptr){;}

Jusqu'à ce que vous puissiez migrer vers C++ 0x, je dirais que c'est une perte de temps de savoir laquelle de ces méthodes vous utilisez, elles sont toutes insuffisantes. C'est pourquoi nullptr a été inventé (ainsi que des problèmes de programmation génériques qui ont abouti à un transfert parfait .) Le plus important est de maintenir la cohérence.

En C

C est une bête différente.

En C, la valeur NULL peut être définie sur 0 ou sur ((void *) 0), C99 permet la mise en œuvre de constantes de pointeur nul définies. Cela revient donc à la définition de NULL de l'implémentation et vous devrez l'inspecter dans votre bibliothèque standard.

Les macros sont très courantes et sont généralement utilisées pour combler les lacunes de la prise en charge de la programmation générique dans le langage, entre autres. Le langage est beaucoup plus simple et le recours au pré-processeur plus commun.

Dans cette perspective, je recommanderais probablement d’utiliser la définition de macro NULL dans C.

24
M2tM

J'utilise if (ptr), mais cela ne vaut absolument pas la peine de discuter.

J'aime mon chemin parce que c'est concis, même si d'autres disent que == NULL le rend plus facile à lire et plus explicite. Je vois d'où ils viennent, je ne suis pas du tout d'accord avec le fait que des trucs supplémentaires facilitent les choses. (Je déteste la macro, alors je suis partial.) À vous de voir.

Je ne suis pas d'accord avec votre argument. Si vous ne recevez pas d'avertissements pour des assignations conditionnelles, vous devez augmenter votre niveau d'avertissement. Aussi simple que cela. (Et pour l'amour de tout ce qui est bon, ne les changez pas.)

Note en C++ 0x, nous pouvons faire if (ptr == nullptr), ce qui pour moi fait est plus agréable à lire. (Encore une fois, je déteste la macro. Mais nullptr c'est Nice.) Je fais toujours if (ptr), juste parce que c'est ce à quoi je suis habitué.

19
GManNickG

Franchement, je ne vois pas pourquoi c'est important. L’un ou l’autre est assez clair et toute personne ayant une expérience moyenne du C ou du C++ devrait comprendre les deux. Un commentaire, cependant:

Si vous envisagez de reconnaître l'erreur et de ne pas continuer à exécuter la fonction (c'est-à-dire que vous allez lever une exception ou renvoyer un code d'erreur immédiatement), vous devez en faire une clause de protection:

int f(void* p)
{
    if (!p) { return -1; }

    // p is not null
    return 0;
}

De cette façon, vous évitez le "code de flèche".

9
James McNellis

Encore un point en faveur de la pratique foo == NULL: Si foo est, par exemple, un int * ou un bool *, alors la vérification if (foo) peut être accidentellement interprété par un lecteur comme testant la valeur de la pointee, c'est-à-dire comme if (*foo). La comparaison NULL rappelle que nous parlons d'un pointeur.

Mais je suppose qu'une bonne convention de nommage rend cet argument théorique.

7
Daniel Hershcovich

Personnellement, j'ai toujours utilisé if (ptr == NULL) parce que cela explicite mon intention, mais à ce stade, ce n'est qu'une habitude.

L'utilisation de = à la place de == sera interceptée par tout compilateur compétent disposant des paramètres d'avertissement corrects.

L'important est de choisir un style cohérent pour votre groupe et de vous y tenir. Peu importe le chemin que vous prenez, vous finirez par vous y habituer, et la perte de friction lorsque vous travaillez dans le code d'autres personnes sera la bienvenue.

7
Mark Ransom

En fait, j'utilise les deux variantes.

Il existe des situations dans lesquelles vous vérifiez d'abord la validité d'un pointeur, et si c'est NULL, 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, faites ce que vous voulez et résolvez le cas d'erreur. Le résultat peut être le code indenté x-times moche avec plusieurs si.

4
dwo

Le langage de programmation C (K & R) vous demanderait de vérifier null == ptr pour éviter une affectation accidentelle.

4
Derek

Si le style et le format doivent faire partie de vos examens, vous devez définir un guide de style sur lequel vous pourrez vous mesurer. S'il y en a un, faites ce que dit le guide de style. S'il n'y en a pas, les détails comme celui-ci doivent être laissés tels qu'ils sont écrits. C'est une perte de temps et d'énergie, qui détourne l'attention de ce que les revues de code devraient vraiment révéler. Sérieusement, sans guide de style, je ne voudrais pas changer le code comme ceci par principe, même s'il n'utilise pas la convention que je préfère.

Et pas que cela compte, 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 est préférable de gérer les conditions d'erreur avant le chemin heureux? Dans ce cas, je ne suis toujours pas d'accord avec le critique. Je ne sais pas s'il existe une convention acceptée pour cela, mais à mon avis, la condition la plus "normale" devrait être la première des déclarations if. De cette façon, j'ai moins de travail à faire pour comprendre en quoi consiste la fonction et comment elle fonctionne.

L'exception à cette règle est si l'erreur me force à quitter la fonction ou à en récupérer avant de passer à autre chose. Dans ces cas, je traite 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 de la situation inhabituelle dès le départ, je peux m'en occuper puis l'oublier. Mais si je ne peux pas revenir sur le bon chemin en le traitant à l'avance, alors il devrait être traité après le cas principal car cela rend le code plus compréhensible. À mon avis.

Mais si ce n'est pas dans un guide de style, c'est simplement mon avis et celui-ci est tout aussi valable. Normaliser ou ne pas. Ne laissez pas un critique pseudo-standardiser simplement parce qu'il a une opinion.

3
Darryl

La plupart des compilateurs que j'ai utilisés avertiront au moins sur l'assignation if sans autre sucre syntaxique, aussi je n'achète pas cet argument. Cela dit, j'ai utilisé les deux à la fois de manière professionnelle et je n’ai aucune préférence pour l’un ou l’autre. Le == NULL est bien plus clair à mon avis.

1
Michael Dorgan
  • Les pointeurs ne sont pas des booléens
  • Les compilateurs C/C++ modernes émettent un avertissement lorsque vous écrivez if (foo = bar) par accident.

Donc 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 n’y mettrais pas des choses comme celle-ci, mais des choses comme:

Assurez-vous de faire une vérification nulle sur le pointeur.

1
JeremyP

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 risque d’assignations accidentelles.

1
sth

Je suis un grand fan du fait que C/C++ ne vérifie pas les types dans les conditions booléennes dans les instructions if, for et while. J'utilise toujours ce qui suit:

if (ptr)

if (!ptr)

même sur des entiers ou un autre type convertissant 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, alors que je travaillais sur le microcontrôleur OKI 431, j’ai remarqué ce qui suit:

unsigned char chx;

if (chx) // ...

est plus efficace que

if (chx == 1) // ...

parce que dans les derniers cas, le compilateur doit comparer la valeur de chx à 1. Où chx est juste un drapeau vrai/faux.

1
Donotalo

C’est l’un des principes fondamentaux des deux langages: les pointeurs évaluent un type et une valeur pouvant être utilisés comme expression de contrôle, bool en C++ et int en C. Utilisez-le.

1
Jens Gustedt