Puis-je utiliser NULL comme substitution pour la valeur de 0?


73

Suis-je autorisé à utiliser le NULLpointeur en remplacement de la valeur de 0?

Ou y a-t-il quelque chose de mal à faire cela?


Comme par exemple:

int i = NULL;

en remplacement de:

int i = 0;

Comme expérience, j'ai compilé le code suivant:

#include <stdio.h>

int main(void)
{
    int i = NULL;
    printf("%d",i);

    return 0;
}

Production:

0

En effet, il me donne cet avertissement, qui est tout à fait correct en soi:

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 

mais le résultat est toujours équivalent.


  • Suis-je en train de passer à "Comportement indéfini" avec ça?
  • Est-il permis d'utiliser NULLde cette manière?
  • Y a-t-il quelque chose de mal à utiliser NULLcomme valeur numérique dans les expressions arithmétiques?
  • Et quel est le résultat et le comportement en C ++ dans ce cas?

J'ai lu les réponses de Quelle est la différence entre NULL, '\ 0' et 0 à propos de la différence entre NULL, \0et 0est, mais je n'ai pas obtenu les informations concises à partir de là, si c'est tout à fait autorisé et aussi juste d'utiliser NULLcomme valeur à utiliser dans les affectations et autres opérations arithmétiques.


Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Samuel Liew

Il serait vraiment préférable de poser deux questions distinctes, une pour C et une pour C ++.
Konrad Rudolph

Réponses:


82

Suis-je autorisé à utiliser le pointeur NULL en remplacement de la valeur 0?

Non , ce n'est pas sûr de le faire. NULLest une constante de pointeur nul, qui pourrait avoir un type int, mais qui a plus généralement un type void *(en C), ou autrement n'est pas directement assignable à un int(en C ++> = 11). Les deux langages permettent aux pointeurs d'être convertis en entiers, mais ils ne prévoient pas que ces conversions soient effectuées implicitement (bien que certains compilateurs fournissent cela comme une extension). De plus, bien qu'il soit courant de convertir un pointeur nul en un entier pour donner la valeur 0, la norme ne le garantit pas. Si vous voulez une constante de type intet de valeur 0, épelez-la 0.

  • Suis-je susceptible de passer à un comportement indéfini avec cela?

Oui, sur toute implémentation où se NULLdéveloppe en une valeur de type void *ou toute autre non directement assignable int. La norme ne définit pas le comportement de votre affectation sur une telle implémentation, ergo son comportement n'est pas défini.

  • est-il permis de fonctionner avec le NULL de cette façon?

C'est un style médiocre et il se cassera sur certains systèmes et dans certaines circonstances. Dans la mesure où vous semblez utiliser GCC, cela casserait votre propre exemple si vous compiliez avec l' -Werroroption.

  • Y a-t-il quelque chose de mal à utiliser NULL comme valeur numérique dans les expressions arithmétiques?

Oui. Il n'est pas du tout garanti d'avoir une valeur numérique. Si vous voulez dire 0, écrivez 0, ce qui est non seulement bien défini, mais plus court et plus clair.

  • Et comment est le résultat en C ++ dans ce cas?

Le langage C ++ est plus strict sur les conversions que C et a des règles différentes pour NULL, mais là aussi, les implémentations peuvent fournir des extensions. Encore une fois, si vous voulez dire 0, c'est ce que vous devez écrire.


4
Vous devez souligner que "qui a généralement un type void *" n'est vrai que pour C. void *n'est pas un type légal pour C ++ (car vous ne pouvez pas l'affecter void*à un autre type de pointeur). En C ++ 89 et C ++ 03, il NULL doit en fait être de type int, mais dans les versions ultérieures il peut l'être (et l'est généralement) nullptr_t.
Martin Bonner soutient Monica

Vous vous trompez également lors de la conversion void*en intun comportement non défini. Ce n'est pas; il s'agit d'un comportement spécifié par l'implémentation.
Martin Bonner soutient Monica

@MartinBonnersupportsMonica, Dans les contextes où C spécifie qu'un pointeur est converti en un entier, le résultat de la conversion est en effet spécifié par l'implémentation, mais ce n'est pas ce dont je parle. Il s'agit de l'affectation d'un pointeur à une valeur l de type entier (sans conversion explicite via un transtypage) qui a un comportement indéfini. Le langage n'y définit pas de conversion automatique.
John Bollinger

@MartinBonnersupportsMonica, j'ai modifié pour être plus inclusif des considérations C ++. Dans tous les cas, le thème central s'applique également aux deux langues: si vous voulez un entier 0, écrivez-le explicitement en tant que constante entière de type approprié.
John Bollinger

31

NULLest une constante de pointeur nul. En C, il pourrait s'agir d'une expression constante entière avec valeur 0ou d'une telle expression transtypée void*, cette dernière étant plus probable. Ce qui signifie que vous ne pouvez pas supposer utiliser de NULLmanière interchangeable avec zéro. Par exemple, dans cet exemple de code

char const* foo = "bar"; 
foo + 0;

Le remplacement 0par NULLn'est pas garanti être un programme C valide, car l'addition entre deux pointeurs (sans parler de types de pointeurs différents) n'est pas définie. Cela entraînera l'émission d'un diagnostic en raison d'une violation de contrainte. Les opérandes à ajouter ne seront pas valides .


Quant au C ++, les choses sont quelque peu différentes. L'absence d'une conversion implicite de void*vers d'autres types d'objets signifiait qu'elle NULLétait historiquement définie comme 0dans le code C ++. En C ++ 03, vous pourriez probablement vous en tirer. Mais depuis C ++ 11, il peut êtrenullptr légalement défini comme mot clé . Produisant à nouveau une erreur, car std::nullptr_tne peut pas être ajouté aux types de pointeurs.

Si NULLest défini comme tel, nullptrmême votre expérience devient invalide. Il n'y a pas de conversion de std::nullptr_ten entier. C'est pourquoi elle est considérée comme une constante de pointeur nul plus sûre.


Pour être complet, 0L est également une constante de pointeur nul et peut être utilisé comme NULLdans les deux langues.
eerorika

1
@jamesqf La norme indique que la constante entière avec la valeur 0 est une constante de pointeur nul. Par conséquent, 0L est une constante de pointeur nul.
eerorika

1
@eerorika: Exactement ce dont le monde a besoin, des normes qui ignorent la réalité :-) Parce que si je me souviens bien de mon assemblage 80286, vous ne pouvez même pas assigner un pointeur éloigné en une seule opération, donc les rédacteurs du compilateur devraient -le cas.
jamesqf

2
@jamesqf Selon la FAQ C , re: faire 0une constante de pointeur nul: "évidemment comme un sop à tout le code C mal écrit existant qui a fait des hypothèses incorrectes"
Andrew Henle

3
@jamesqf, que toute constante entière avec la valeur 0 est une constante de pointeur nul (en C) n'a rien à voir avec les implémentations matérielles de pointeur. Notez également que la norme C ne reconnaît en aucun cas une distinction entre les pointeurs proches et éloignés, mais prend en charge les affectations de pointeur à pointeur. Il prend également en charge (certaines) les comparaisons de pointeurs, qui présentent des problèmes intéressants pour les formats d'adressage segmenté tels que les 286.
John Bollinger

21

Suis-je autorisé à utiliser le pointeur NULL en remplacement de la valeur 0?

int i = NULL;

Les règles varient selon les langues et leurs versions. Dans certains cas, vous pouvez et dans d'autres, vous ne pouvez pas. Quoi qu'il en soit, vous ne devriez pas . Si vous êtes chanceux, votre compilateur vous avertira lorsque vous tenterez de le faire ou mieux encore, ne parviendra pas à compiler.

En C ++, avant C ++ 11 (citation de C ++ 03):

[lib.support.types]

NULL est une constante de pointeur null C ++ définie par l'implémentation dans la présente Norme internationale.

Cela n'a pas de sens d'utiliser une constante de pointeur nul comme entier. Toutefois...

[conv.ptr]

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

Donc, cela fonctionnerait techniquement même si c'est absurde. En raison de cette technicité, vous pouvez rencontrer des programmes mal écrits qui abusent NULL.

Depuis C ++ 11 (citation du dernier projet):

[conv.ptr]

Une constante de pointeur null est un littéral entier ([de lex.icon]) avec la valeur zéro ou un prvalue de type std :: nullptr_t .

A std​::​nullptr_­tn'est pas convertible en entier, donc l'utilisation en NULLtant qu'entier ne fonctionnerait que de manière conditionnelle, selon les choix effectués par l'implémentation du langage.

PS nullptrest une valeur de type std​::​nullptr_­t. Sauf si vous avez besoin de votre programme pour compiler en pré-C ++ 11, vous devez toujours utiliser à la nullptrplace de NULL.


C est un peu différent (citations du projet C11 N1548):

6.3.2.3 Langue / Conversions / Autres opérandes / Pointeurs

3 Une expression de constante entière avec la valeur 0, ou une telle expression transtypée en typevoid * , est appelée constante de pointeur nul. ...

Ainsi, le cas est similaire au post C ++ 11 c'est-à-dire l'abus de NULLtravaux conditionnellement en fonction des choix faits par l'implémentation du langage.


10

Oui , mais selon l'implémentation, vous aurez peut-être besoin d'un casting. Mais oui, c'est 100% légitime, sinon.

Bien que ce soit vraiment, vraiment, vraiment mauvais style (inutile de dire?).

NULLest, ou était, en fait pas C ++, il est C. La norme ne cependant, comme pour de nombreux legs C, ont deux clauses ([diff.null] et [support.types.nullptr]) qui font techniquement NULLC ++. Il s'agit d'une constante de pointeur nul définie par l'implémentation . Par conséquent, même si c'est un mauvais style, c'est techniquement aussi C ++ que possible.
Comme indiqué dans la note de bas de page , les implémentations possibles pourraient être 0ou 0L, mais pas (void*)0 .

NULLpourrait, bien sûr (la norme ne le dit pas explicitement, mais c'est à peu près le seul choix restant après 0ou 0L) être nullptr. Ce n'est presque jamais le cas, mais c'est une possibilité légale.

L'avertissement que le compilateur vous a montré montre que le compilateur n'est en fait pas conforme (sauf si vous avez compilé en mode C). Parce que, eh bien, selon l'avertissement, il a converti un pointeur nul (non nullptrqui serait de nullptr_t, qui serait distinct), donc apparemment la définition de NULLest en effet (void*)0, ce qu'elle peut ne pas être.

Quoi qu'il en soit, vous avez deux cas légitimes possibles (c'est-à-dire que le compilateur n'est pas cassé). Soit (le cas réaliste), NULLest quelque chose comme 0ou 0L, alors vous avez des conversions "zéro ou un" en entier, et vous êtes prêt à partir.

Ou NULLest en effet nullptr. Dans ce cas, vous avez une valeur distincte qui offre des garanties de comparaison ainsi que des conversions clairement définies à partir d' entiers, mais malheureusement pas à des entiers. Il a cependant une conversion clairement définie en bool(résultant en false) et boolune conversion clairement définie en entier (résultant en 0).

Malheureusement, il s'agit de deux conversions, donc ce n'est pas dans "zéro ou un" comme indiqué dans [conv]. Ainsi, si votre implémentation se définit NULLcomme nullptr, vous devrez ajouter un cast explicite pour que votre code soit correct.


6

De la faq C:

Q: Si NULL et 0 sont équivalents en tant que constantes de pointeur nul, que dois-je utiliser?

R: Il est seulement dans des contextes de pointeur NULLet 0sont équivalents. neNULL doit pas être utilisé lorsqu'un autre type de 0 est requis, même s'il peut fonctionner, car cela envoie le mauvais message stylistique. (De plus, ANSI autorise la définition de NULL ((void *)0), qui ne fonctionnera pas du tout dans des contextes sans pointeur.) En particulier, ne pas utiliser NULLlorsque le caractère nul ASCII (NUL) est souhaité. Donnez votre propre définition

http://c-faq.com/null/nullor0.html


5

Avertissement: je ne connais pas le C ++. Ma réponse n'est pas destinée à être appliquée dans le contexte de C ++

'\0'est un intavec une valeur nulle, juste 100% exactement comme 0.

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

Dans le cadre des pointeurs , 0et NULLsont 100% équivalents:

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

sont tous 100% équivalents.


Remarque sur ptr + NULL

Le contexte de ptr + NULLn'est pas celui des pointeurs. Il n'y a pas de définition pour l'ajout de pointeurs dans le langage C; des pointeurs et des entiers peuvent être ajoutés (ou soustraits). Dans le ptr + NULLcas où l'un ptrou l' autre NULLest un pointeur, l'autre doit être un entier, donc ptr + NULLest effectivement (int)ptr + NULLou ptr + (int)NULLet selon les définitions de ptret NULLplusieurs comportements peuvent être attendus: tout fonctionne, avertissement de conversion entre pointeur et entier, échec de compilation, .. .


Je l'ai déjà vu #define NULL (void *)0. Êtes-vous sûr que NULL et plain 0 sont 100% équivalents?
machine_1

2
Dans le contexte du pointeur, oui ... condition soulignée dans ma réponse, merci
pmg

@phuclv: Je n'ai absolument aucune idée de C ++. Ma réponse (sauf le bit entre parenthèses) est à propos de C
pmg

@phuclv ptr + NULLn'utilise pas NULLdans le contexte des pointeurs
pmg

3
@JesperJuhl: dans le contexte des pointeurs, ils sont 100% équivalents. Je n'ai aucune idée de ce qui nullptrest, mais ((void*)0)et 0(ou '\0') sont équivalents dans le contexte des pointeurs ...if (ptr == '\0' /* or equivalent 0, NULL */)
pmg

5

Non, NULLje ne préfère plus utiliser (ancienne méthode d'initialisation du pointeur).

Depuis C ++ 11:

Le mot clé nullptrdésigne le littéral du pointeur. C'est une valeur de type std :: nullptr_t. Il existe des conversions implicites de nullptren valeur de pointeur nulle de tout type de pointeur et de tout pointeur en type de membre. Des conversions similaires existent pour toute constante de pointeur nul, qui inclut des valeurs de type std::nullptr_tainsi que la macro NULL.

https://en.cppreference.com/w/cpp/language/nullptr

En fait, std :: nullptr_t est le type du littéral de pointeur NULL, nullptr. Il s'agit d'un type distinct qui n'est pas lui-même un type de pointeur ou un pointeur sur le type de membre.

#include <cstddef>
#include <iostream>

void f(int* pi)
{
   std::cout << "Pointer to integer overload\n";
}

void f(double* pd)
{
   std::cout << "Pointer to double overload\n";
}

void f(std::nullptr_t nullp)
{
   std::cout << "null pointer overload\n";
}

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(0);  // ambiguous call: all three functions are candidates
    // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                // (as is the case in most implementations)
}

Production:

Pointer to integer overload
Pointer to double overload
null pointer overload

La question concerne l'affectation de NULL à 0 pour les entiers. En ce sens, rien ne change avec nullptr au lieu de NULL.
ivan.ukr

Il a utilisé des mots comme "pointeur NULL".
Mannoj

Soit dit en passant, C ++ n'a pas le concept NULL après C ++ 11. L'auteur peut être confus quant à l'utilisation de constexpr ou définir une initialisation à l'ancienne. en.cppreference.com/w/cpp/language/default_initialization
Mannoj
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.