Quelle est la différence entre NULL, '\ 0' et 0?

309

En C, il semble y avoir des différences entre les différentes valeurs de zéro - NULL, NULet 0.

Je sais que le caractère ASCII correspond '0'à 48ou 0x30.

Le NULLpointeur est généralement défini comme:

#define NULL 0

Ou

#define NULL (void *)0

De plus, il y a le NULpersonnage '\0'qui semble également évaluer 0.

Y a-t-il des moments où ces trois valeurs ne peuvent pas être égales?

Est-ce également vrai sur les systèmes 64 bits?

gnavi
la source
1
Voir stackoverflow.com/questions/176989/… pour plus d'informations sur les différences entre 0 et NULL.
David Rodríguez - dribeas
7
L'identifiant NULn'existe pas dans le langage ou la bibliothèque standard C (ou en C ++ pour autant que je sache). Le caractère nul est parfois appelé NUL, mais il est généralement appelé C ou C ++ '\0'.
Keith Thompson

Réponses:

351

Remarque: Cette réponse s'applique au langage C, pas à C ++.


Pointeurs nuls

Le littéral de constante entière 0a des significations différentes selon le contexte dans lequel il est utilisé. Dans tous les cas, c'est toujours une constante entière avec la valeur0 , elle est juste décrite de différentes manières.

Si un pointeur est comparé au littéral constant 0, il s'agit d'une vérification pour voir si le pointeur est un pointeur nul. Ceci 0est alors appelé une constante de pointeur nul. La norme C définit que la 0conversion en type void *est à la fois un pointeur nul et une constante de pointeur nul.

De plus, pour améliorer la lisibilité, la macro NULL est fournie dans le fichier d'en-tête stddef.h. Selon votre compilateur, il peut être possible de le #undef NULLredéfinir en quelque chose de farfelu.

Par conséquent, voici quelques moyens valides de rechercher un pointeur nul:

if (pointer == NULL)

NULLest défini pour comparer égal à un pointeur nul. C'est l'implémentation définie quelle est la définition réelle de NULL, tant qu'il s'agit d'une constante de pointeur null valide.

if (pointer == 0)

0 est une autre représentation de la constante de pointeur nul.

if (!pointer)

Cette ifinstruction vérifie implicitement "n'est pas 0", donc nous inversons cela pour signifier "est 0".

Les méthodes suivantes sont INVALIDES pour rechercher un pointeur nul:

int mynull = 0;
<some code>
if (pointer == mynull)

Pour le compilateur, ce n'est pas une vérification pour un pointeur nul, mais une vérification d'égalité sur deux variables. Cela peut fonctionner si mynull ne change jamais dans le code et la constante d'optimisation du compilateur replie le 0 dans l'instruction if, mais cela n'est pas garanti et le compilateur doit produire au moins un message de diagnostic (avertissement ou erreur) selon la norme C.

Notez que ce qui est un pointeur nul dans le langage C. Peu importe l'architecture sous-jacente. Si l'architecture sous-jacente a une valeur de pointeur nulle définie comme adresse 0xDEADBEEF, alors c'est au compilateur de trier ce gâchis.

En tant que tel, même sur cette architecture amusante, les moyens suivants sont toujours des moyens valides de rechercher un pointeur nul:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)

Les méthodes suivantes sont INVALIDES pour rechercher un pointeur nul:

#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)

car ceux-ci sont vus par un compilateur comme des comparaisons normales.

Personnages nuls

'\0'est défini comme un caractère nul - c'est-à-dire un caractère dont tous les bits sont mis à zéro. Cela n'a rien à voir avec les pointeurs. Cependant, vous pouvez voir quelque chose de similaire à ce code:

if (!*string_pointer)

vérifie si le pointeur de la chaîne pointe sur un caractère nul

if (*string_pointer)

vérifie si le pointeur de la chaîne pointe sur un caractère non nul

Ne les confondez pas avec des pointeurs nuls. Tout simplement parce que la représentation des bits est la même, et cela permet des cas de croisement pratiques, ce n'est pas vraiment la même chose.

De plus, '\0'est (comme tous les littéraux de caractères) une constante entière, dans ce cas avec la valeur zéro. '\0'Est donc complètement équivalent à une 0constante entière sans fioritures - la seule différence est dans l' intention qu'elle transmet à un lecteur humain ("J'utilise ceci comme un caractère nul.").

Références

Voir la question 5.3 de la FAQ comp.lang.c pour plus d'informations. Voir ce pdf pour la norme C. Consultez les sections 6.3.2.3 Pointeurs, paragraphe 3.

Andrew Keeton
la source
3
Merci d'avoir pointé la liste des FAQ. Cependant, voir aussi c-faq.com/null/nullor0.html
Sinan Ünür
4
Non, vous ne pourrez pas comparer ptrà tous les bits à zéro . Ce n'est pas un memcmp, mais c'est une comparaison utilisant un opérateur intégré. L'un côté est une constante de pointeur nul '\0'et l'autre côté est un pointeur. Ainsi qu'avec les deux autres versions avec NULLet 0. Ces trois-là font la même chose.
Johannes Schaub - litb
6
Vous prenez l'opérateur de comparaison intégré comme une chose qui comparerait des chaînes de bits. Mais ce n'est pas ça. Il compare deux valeurs, qui sont des concepts abstraits. Ainsi , un pointeur NULL qui est interne représentée comme 0xDEADBEEFtoujours un pointeur NULL, peu importe ce que ses regards aiment chaîne binaire, et il comparera toujours égal à NULL, 0, \0et toutes les autres formes de constantes pointeur NULL.
Johannes Schaub - litb
2
Vous faites un bon point sur l'opérateur de comparaison. J'ai révisé C99. Il dit "Une expression constante entière avec la valeur 0, ou une telle expression transtypée pour taper void *, est appelée une constante de pointeur nul." Il indique également qu'un littéral de caractère est une expression constante entière. Ainsi, par la propriété transitive, vous avez raison ptr == '\0'.
Andrew Keeton
2
".... il pourrait être possible de #undef NULL et de le redéfinir en quelque chose de farfelu. Quiconque fait cela mérite d'être abattu." ceci mon bon monsieur m'a fait rire à haute voix ...
oggiemc
34

Il semble qu'un certain nombre de personnes comprennent mal les différences entre NULL, «\ 0» et 0. Donc, pour expliquer, et pour éviter de répéter les choses, on a dit plus tôt:

Une expression constante de type intavec la valeur 0, ou une expression de ce type, transtypée en type, void *est une constante de pointeur nulle qui, si elle est convertie en pointeur, devient un pointeur nul. . Il est garanti par la norme de comparer l' inégalité à tout pointeur à tout objet ou fonction .

NULL est une macro, définie en tant que constante de pointeur nul .

\0est une construction utilisée pour représenter le caractère nul , utilisée pour terminer une chaîne.

Un caractère nul est un octet dont tous ses bits sont mis à 0.

amaterasu
la source
14

Les trois définissent la signification de zéro dans un contexte différent.

  • contexte du pointeur - NULL est utilisé et signifie que la valeur du pointeur est 0, indépendamment du fait qu'il soit 32 bits ou 64 bits (un cas 4 octets les 8 autres octets de zéros).
  • contexte de chaîne - le caractère représentant le chiffre zéro a une valeur hexadécimale de 0x30, tandis que le caractère NUL a une valeur hexadécimale de 0x00 (utilisé pour terminer les chaînes).

Ces trois sont toujours différents quand on regarde la mémoire:

NULL - 0x00000000 or 0x00000000'00000000 (32 vs 64 bit)
NUL - 0x00 or 0x0000 (ascii vs 2byte unicode)
'0' - 0x20

J'espère que cela clarifie les choses.

Nasko
la source
8
Nasko: Évaluez sizeof('\0')et soyez surpris.
caf
3
@Nasko: J'ai été vraiment surpris: avec gcc, en C: sizeof ('\ 0') == sizeof ('a') == 4, tandis qu'avec g ++, en C ++: sizeof ('\ 0') == sizeof ('a') == 1
David Rodríguez - dribeas
1
@Nasko: D'après la norme C (brouillon, n1124): 'Une constante de caractère entier a le type int', donc '\ 0' est en fait de type int en C, et donc sizeof ('\ 0') est 4 dans mon architecture (linux, 32bit)
David Rodríguez - dribeas
@dribeas - Je ne le décrivais pas comme une constante, plutôt ce que vous verriez comme faisant partie de la chaîne. J'aurais certainement pu le rendre explicite. Merci
Nasko
@ DavidRodríguez-dribeas Undid edit "Valeur ASCII '0' corrigée à 0x20 (déc 32)"
chux - Rétablir Monica le
6

Si NULL et 0 sont équivalents en tant que constantes de pointeur nul, que dois-je utiliser? dans la liste FAQ C résout également ce problème:

Les programmeurs C doivent comprendre cela NULLet 0sont interchangeables dans des contextes de pointeurs, et qu'une non-diffusion 0 est parfaitement acceptable. Toute utilisation de NULL (par opposition à 0) doit être considérée comme un léger rappel qu'un pointeur est impliqué; les programmeurs ne devraient pas en dépendre (soit pour leur propre compréhension, soit pour celle du compilateur) pour distinguer les pointeurs 0des entiers0 .

Ce n'est que dans des contextes de pointeurs qui sont NULLet 0sont équivalents. NULLne doit pas être utilisé lorsqu'un autre type de fichier 0est requis, même s'il peut fonctionner, car cela envoie le mauvais message stylistique. (De plus, ANSI autorise la définition de NULLbe ((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

#define NUL '\0'

si tu dois.

Sinan Ünür
la source
5

Quelle est la différence entre NULL, '\ 0' et 0

"caractère nul (NUL)" est plus facile à exclure. '\0'est un caractère littéral. En C, il est implémenté comme int, donc, c'est la même chose que 0, qui est de INT_TYPE_SIZE. En C ++, le littéral de caractères est implémenté en tant que char, ce qui correspond à 1 octet. Ceci est normalement différent de NULLou0 .

Prochain, NULL est une valeur de pointeur qui spécifie qu'une variable ne pointe vers aucun espace d'adressage. Mis à part le fait qu'il est généralement implémenté sous forme de zéros, il doit pouvoir exprimer l'espace d'adressage complet de l'architecture. Ainsi, sur une architecture 32 bits NULL (probable) est de 4 octets et sur une architecture 64 bits de 8 octets. Cela dépend de la mise en œuvre de C.

Enfin, le littéral 0est de type int, qui est de taille INT_TYPE_SIZE. La valeur par défaut de INT_TYPE_SIZEpourrait être différente selon l'architecture.

Apple a écrit:

Le modèle de données 64 bits utilisé par Mac OS X est appelé "LP64". Il s'agit du modèle de données commun utilisé par d'autres systèmes UNIX 64 bits de Sun et SGI ainsi que Linux 64 bits. Le modèle de données LP64 définit les types primitifs comme suit:

  • les entiers sont 32 bits
  • les longs sont 64 bits
  • les longs-longs sont également 64 bits
  • les pointeurs sont 64 bits

Wikipédia 64 bits :

Le compilateur VC ++ de Microsoft utilise le modèle LLP64.

64-bit data models
Data model short int long  long long pointers Sample operating systems
LLP64      16    32  32    64        64       Microsoft Win64 (X64/IA64)
LP64       16    32  64    64        64       Most Unix and Unix-like systems (Solaris, Linux, etc.)
ILP64      16    64  64    64        64       HAL
SILP64     64    64  64    64        64       ?

Edit : Ajout de plus sur le littéral de caractère.

#include <stdio.h>

int main(void) {
    printf("%d", sizeof('\0'));
    return 0;
}

Le code ci-dessus renvoie 4 sur gcc et 1 sur g ++.

Eugene Yokota
la source
2
Non, '\0'n'est pas une valeur à 1 octet. C'est un littéral de caractère, qui est une expression constante entière - donc s'il peut être dit qu'il a une taille, c'est la taille d'un int(qui doit être d'au moins 2 octets). Si vous ne me croyez pas, évaluez sizeof('\0')et voyez par vous-même. '\0', 0Et 0x0sont tous complètement équivalents.
caf
@caf cela dépend de la langue. Si vous ne me croyez pas, essayez sizeof('\0')un compilateur C ++.
Eugene Yokota
2
vous devez utiliser "% zu" lors de l'impression de sizeof (quelque chose)
Non utilisé
4

Un NUL d'un L, il termine une chaîne.

Un NULL de deux L ne signifie rien.

Et je parierai un taureau d'or

Qu'il n'y a pas de NULLL de trois litres.

Comment gérez-vous NUL?

EvilTeach
la source
4

Un bon morceau qui m'aide lorsque je commence par C (extrait de la programmation Expert C de Linden)

Le One 'l' nul et le Two 'l' null

Mémorisez cette petite rime pour rappeler la terminologie correcte pour les pointeurs et zéro ASCII:

The one "l" NUL ends an ASCII string,

The two "l" NULL points to no thing.

Apologies to Ogden Nash, but the three "l" nulll means check your spelling. 

Le caractère ASCII avec la configuration binaire de zéro est appelé "NUL". La valeur spéciale du pointeur qui signifie que le pointeur n'est nulle part "NULL". Les deux termes ne sont pas interchangeables dans leur sens.

dlmeetei
la source
Beaucoup plus simple: NULest un code de commande tel que BEL, VT, HT, SOTetc. et a donc max. 3 caractères.
glglgl
2

"NUL" n'est pas 0, mais fait référence au caractère ASCII NUL. C'est du moins ainsi que je l'ai vu utilisé. Le pointeur nul est souvent défini sur 0, mais cela dépend de l'environnement dans lequel vous exécutez et de la spécification du système d'exploitation ou de la langue que vous utilisez.

Dans ANSI C, le pointeur nul est spécifié comme la valeur entière 0. Ainsi, tout monde où ce n'est pas vrai n'est pas conforme à ANSI C.

peterb
la source
1

Un octet avec une valeur de 0x00est, sur la table ASCII, le caractère spécial appelé NULou NULL. En C, puisque vous ne devez pas incorporer de caractères de contrôle dans votre code source, cela est représenté dans les chaînes C avec un 0 d'échappement, c'est-à-dire \0.

Mais un vrai NULL n'est pas une valeur. C'est l'absence de valeur. Pour un pointeur, cela signifie que le pointeur n'a rien à pointer. Dans une base de données, cela signifie qu'il n'y a pas de valeur dans un champ (ce qui n'est pas la même chose que de dire que le champ est vide, 0 ou rempli d'espaces).

La valeur réelle qu'un format de fichier système ou base de données donné utilise pour représenter un NULLn'est pas nécessairement 0x00.

richardtallent
la source
0

NULLn'est pas garanti comme étant 0 - sa valeur exacte dépend de l'architecture. La plupart des architectures majeures le définissent (void*)0.

'\0' sera toujours égal à 0, car c'est ainsi que l'octet 0 est codé dans un littéral de caractère.

Je ne me souviens pas si les compilateurs C sont tenus d'utiliser ASCII - sinon, ils '0'ne sont pas toujours égaux à 48. Quoi qu'il en soit, il est peu probable que vous rencontriez un système qui utilise un jeu de caractères alternatif comme EBCDIC à moins que vous ne travailliez très systèmes obscurs.

Les tailles des différents types différeront sur les systèmes 64 bits, mais les valeurs entières seront les mêmes.


Certains commentateurs ont exprimé des doutes que NULL soit égale à 0, mais pas être nul. Voici un exemple de programme, ainsi que les résultats attendus sur un tel système:

#include <stdio.h>

int main () {
    size_t ii;
    int *ptr = NULL;
    unsigned long *null_value = (unsigned long *)&ptr;
    if (NULL == 0) {
        printf ("NULL == 0\n"); }
    printf ("NULL = 0x");
    for (ii = 0; ii < sizeof (ptr); ii++) {
        printf ("%02X", null_value[ii]); }
    printf ("\n");
    return 0;
}

Ce programme pourrait imprimer:

NULL == 0
NULL = 0x00000001
John Millikin
la source
2
OP demandait «\ 0» (le caractère NUL), pas «0» (le caractère zéro)
Chris Lutz
2
@Chris: '\ 0' n'est pas NULL, c'est l'octet 0 encodé en octal dans un littéral de caractère.
John Millikin
2
En C ++, la norme garantit que la conversion de la valeur entière 0 en un pointeur donnera toujours un pointeur nul. En C ++, 0 est garanti d'être un pointeur nul, tandis que d'un autre côté, NULL est une macro et un codeur malveillant pourrait le redéfinir comme quelque chose de différent.
David Rodríguez - dribeas
6
Et NULL est garanti à 0. La configuration binaire d'un pointeur NULL n'est pas garantie à tous les zéros, mais la constante NULL est, et sera toujours, 0.
jalf
2
Votre première phrase est erronée - NULL ne peut pas être défini comme (void *) 0 en C ++ car il n'y a pas de conversion implicite d'un void * vers un autre pointeur (contrairement à C).
-2

(void *) 0 est NULL et '\ 0' représente la fin d'une chaîne.

shinxg
la source