Posso usare NULL come sostituzione per il valore di 0?


73

Posso usare il NULLpuntatore come sostituto del valore di 0?

O c'è qualcosa di sbagliato in questo fare?


Ad esempio:

int i = NULL;

in sostituzione di:

int i = 0;

Come esperimento ho compilato il seguente codice:

#include <stdio.h>

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

    return 0;
}

Produzione:

0

In effetti mi dà questo avvertimento, che è completamente corretto da solo:

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

ma il risultato è ancora equivalente.


  • Sto attraversando "Undefined Behaviour" con questo?
  • È consentito utilizzare NULLin questo modo?
  • C'è qualcosa di sbagliato nell'usare NULLcome valore numerico nelle espressioni aritmetiche?
  • E qual è il risultato e il comportamento in C ++ per questo caso?

Ho letto le risposte di Qual è la differenza tra NULL, '\ 0' e 0 su quale sia la differenza tra NULL, \0ed 0è, ma non ho ottenuto le informazioni concise da lì, se è del tutto ammissibile e anche giusto usare NULLcome valore per operare con in incarichi e altre operazioni aritmetiche.


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Samuel Liew

Sarebbe davvero meglio porre due domande separate, una per C e una per C ++.
Konrad Rudolph,

Risposte:


82

Sono autorizzato a utilizzare il puntatore NULL come sostituto del valore 0?

No , non è sicuro farlo. NULLè una costante puntatore null, che potrebbe avere tipo int, ma che in genere ha tipo void *(in C), o altrimenti non è direttamente assegnabile a un int(in C ++> = 11). Entrambe le lingue consentono ai puntatori di essere convertiti in numeri interi, ma non prevedono che tali conversioni vengano eseguite implicitamente (sebbene alcuni compilatori lo forniscano come estensione). Inoltre, sebbene sia comune convertire un puntatore null in un numero intero per ottenere il valore 0, lo standard non lo garantisce. Se vuoi una costante con tipo inte valore 0, sillabala 0.

  • Potrei attraversare un comportamento indefinito con questo?

Sì, su qualsiasi implementazione in cui si NULLespande a un valore con tipo void *o qualsiasi altro non direttamente assegnabile a int. Lo standard non definisce il comportamento del tuo incarico su tale implementazione, quindi il suo comportamento non è definito.

  • è consentito operare con il NULL in quel modo?

Ha uno stile scadente e si romperà su alcuni sistemi e in alcune circostanze. Dal momento che sembra che tu stia usando GCC, si romperebbe nel tuo esempio se compilassi con l' -Werroropzione.

  • C'è qualcosa di sbagliato nell'usare NULL come valore numerico nelle espressioni aritmetiche?

Sì. Non è garantito che abbia un valore numerico. Se intendi 0, scrivi 0, che non è solo ben definito, ma più breve e più chiaro.

  • E come è il risultato in C ++ in quel caso?

Il linguaggio C ++ è più severo nelle conversioni rispetto a C e ha regole diverse per NULL, ma anche lì, le implementazioni possono fornire estensioni. Ancora una volta, se vuoi dire 0, allora è quello che dovresti scrivere.


4
Dovresti sottolineare che "che in genere ha un tipo void *" è vero solo per C. void *non è un tipo legale per C ++ (perché non puoi assegnarlo void*a nessun altro tipo di puntatore). In C ++ 89 e C ++ 03, infatti, NULL deve essere di tipo int, ma nelle versioni successive può essere (e di solito lo è) nullptr_t.
Martin Bonner supporta Monica il

Ti sbagli anche la conversione void*in un intcomportamento indefinito. Non è; è un comportamento specificato dall'implementazione.
Martin Bonner supporta Monica il

@MartinBonnersupportsMonica, In quei contesti in cui C specifica che un puntatore viene convertito in un numero intero, il risultato della conversione è effettivamente specificato dall'implementazione, ma non è di questo che sto parlando. È l'assegnazione di un puntatore a un valore di tipo intero (senza conversione esplicita tramite cast) che ha un comportamento indefinito. La lingua non definisce una conversione automatica lì.
John Bollinger,

@MartinBonnersupportsMonica, ho modificato per essere più inclusivo delle considerazioni sul C ++. In ogni caso, il tema centrale si applica ugualmente ad entrambe le lingue: se si desidera un numero intero 0, scriverlo esplicitamente come una costante intera di tipo appropriato.
John Bollinger, il

31

NULLè una costante puntatore null. In C potrebbe essere un'espressione costante intera con valore 0o una tale espressione espressa void*, con quest'ultima più probabile. Ciò significa che non puoi presumere di usare in modo NULLintercambiabile con zero. Ad esempio, in questo esempio di codice

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

La sostituzione 0con NULLnon è garantita per essere un programma C valido, poiché l'aggiunta tra due puntatori (per non parlare dei diversi tipi di puntatore) non è definita. Ciò causerà l'emissione di una diagnostica a causa di una violazione del vincolo. Gli operandi per l'aggiunta non saranno validi .


Per quanto riguarda il C ++, le cose sono leggermente diverse. La mancanza di una conversione implicita da void*altri tipi di oggetti significava che NULLera storicamente definita come 0nel codice C ++. In C ++ 03, potresti probabilmente cavartela. Ma dal C ++ 11 può esserenullptr legalmente definito come parola chiave . Ora di nuovo producendo un errore, poiché std::nullptr_tpotrebbe non essere aggiunto ai tipi di puntatore.

Se NULLè definito come nullptrallora anche il tuo esperimento diventa non valido. Non c'è conversione da std::nullptr_tin un numero intero. Questo è il motivo per cui è considerata una costante puntatore null più sicura.


Per completezza, 0L è anche una costante puntatore null e potrebbe essere utilizzata come NULLin entrambe le lingue.
Eerorika,

1
@jamesqf Lo standard dice che la costante intera con valore 0 è una costante puntatore null. Pertanto 0L è una costante puntatore null.
Eerorika,

1
@eerorika: Proprio ciò di cui il mondo ha bisogno, standard che ignorano la realtà :-) Perché se ricordo correttamente il mio assemblaggio 80286, non puoi nemmeno assegnare un puntatore remoto come un'unica operazione, quindi gli autori del compilatore dovrebbero -coprirlo.
jamesqf il

2
@jamesqf Per le FAQ di C , ri: fare 0una costante puntatore null: "evidentemente come un sop a tutto il codice C mal scritto che ha fatto ipotesi errate"
Andrew Henle,

3
@jamesqf, che qualsiasi costante intera con valore 0 è una costante puntatore null (in C) non ha nulla a che fare con le implementazioni del puntatore hardware. Si noti inoltre che lo standard C non riconosce in ogni caso una distinzione tra puntatori vicini e lontani, ma supporta le assegnazioni da puntatore a puntatore. Supporta anche (alcuni) confronti di puntatori, che presentano interessanti problemi per formati di indirizzamento segmentati come i 286.
John Bollinger,

21

Posso utilizzare il puntatore NULL in sostituzione del valore 0?

int i = NULL;

Le regole variano tra le lingue e le loro versioni. In alcuni casi puoi e in altri no. Indipendentemente da ciò, non dovresti . Se sei fortunato, il tuo compilatore ti avviserà quando lo proverai o, ancora meglio, non riuscirà a compilare.

In C ++, prima di C ++ 11 (citazione da C ++ 03):

[Lib.support.types]

NULL è una costante puntatore null C ++ definita dall'implementazione in questo standard internazionale.

Ha poco senso usare una costante puntatore null come numero intero. Però...

[Conv.ptr]

Una costante puntatore null è un valore di espressione di costante integrale (5.19) di tipo intero che viene valutato a zero.

Quindi, funzionerebbe tecnicamente anche se non ha senso. A causa di questo tecnicismo, potresti incontrare programmi scritti male che abusano NULL.

Dal C ++ 11 (citazione dell'ultima bozza):

[Conv.ptr]

Una costante puntatore nullo è un letterale intero ([lex.icon]) con valore zero o un prvalue di tipo std :: nullptr_t .

A std​::​nullptr_­tnon è convertibile in un numero intero, quindi l'utilizzo NULLcome intero funzionerebbe solo in modo condizionale, a seconda delle scelte fatte dall'implementazione del linguaggio.

PS nullptrè un valore di tipo std​::​nullptr_­t. A meno che non sia necessario compilare il programma in pre-C ++ 11, è necessario utilizzare sempre nullptranziché NULL.


C è un po 'diverso (citazioni dalla bozza C11 N1548):

6.3.2.3 Lingua / Conversioni / Altri operandi / Puntatori

3 Un'espressione di costante intera con il valore 0, o una tale espressione lanciata da digitarevoid * , è chiamata costante di puntatore null. ...

Quindi, il caso è simile al post C ++ 11, ovvero l'abuso di NULLopere condizionatamente a seconda delle scelte fatte dall'implementazione del linguaggio.


10

, anche se a seconda dell'implementazione potrebbe essere necessario un cast. Ma sì, è legittimo al 100%, altrimenti.

Anche se è davvero, davvero, uno stile molto brutto (inutile dirlo?).

NULLè, o era, in realtà non C ++, è C. Lo standard non tuttavia, come per molti legati C, avere due clausole ([diff.null] e [support.types.nullptr]), che rendono tecnicamente NULLC ++. È una costante puntatore null definita dall'implementazione . Pertanto, anche se è un cattivo stile, tecnicamente è C ++ come può essere.
Come sottolineato nella nota a piè di pagina , le possibili implementazioni potrebbero essere 0o 0L, ma non (void*)0 .

NULLpotrebbe, ovviamente (lo standard non lo dice esplicitamente, ma è praticamente l'unica scelta rimasta dopo 0o 0L) essere nullptr. Non è quasi mai il caso, ma è una possibilità legale.

L'avvertimento mostrato dal compilatore dimostra che il compilatore non è in realtà conforme (a meno che non sia stato compilato in modalità C). Perché, bene, secondo l'avvertimento, ha convertito un puntatore nullo (non di nullptrcui sarebbe nullptr_t, che sarebbe distinto), quindi apparentemente la definizione di NULLè effettivamente (void*)0, che potrebbe non essere.

Ad ogni modo, hai due possibili casi legittimi (ovvero compilatore non rotto). O (il caso realistico), NULLè qualcosa di simile 0o 0L, quindi hai "zero o una" conversioni in numero intero e sei a posto.

O lo NULLè davvero nullptr. In tal caso hai un valore distinto che ha garanzie sul confronto e sulle conversioni chiaramente definite da numeri interi, ma sfortunatamente non in numeri interi. Tuttavia, ha una conversione chiaramente definita in bool(risultante false) e boolha una conversione chiaramente definita in intero (risultante 0).

Sfortunatamente, si tratta di due conversioni, quindi non si trova all'interno di "zero o uno", come sottolineato in [conv]. Pertanto, se la tua implementazione lo definisce NULLcome nullptr, allora dovrai aggiungere un cast esplicito affinché il tuo codice sia corretto.


6

Dalla domanda C:

D: Se NULL e 0 sono equivalenti come costanti puntatore null, quale devo usare?

A: È solo in contesti di puntatore che NULLe 0sono equivalenti. nonNULL dovrebbe essere usato quando è richiesto un altro tipo di 0, anche se potrebbe funzionare, perché in questo modo si invia un messaggio stilistico errato. (Inoltre, ANSI consente la definizione di NULL , che non funzionerà affatto in contesti senza puntatore.) In particolare, non utilizzare quando si desidera il carattere null ASCII (NUL). Fornisci la tua definizione((void *)0)NULL

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


5

Disclaimer: non conosco C ++. La mia risposta non è pensata per essere applicata nel contesto di C ++

'\0'è un intvalore zero, esattamente uguale al 100% 0.

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

Nel contesto di puntatori , 0e NULLsono al 100% equivalenti:

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

sono tutti equivalenti al 100%.


Nota circa ptr + NULL

Il contesto di nonptr + NULL è quello dei puntatori. Non esiste una definizione per l'aggiunta di puntatori nel linguaggio C; puntatori e numeri interi possono essere aggiunti (o sottratti). In se uno o è un puntatore, l'altro deve essere un intero, quindi è efficace o e a seconda delle definizioni di e diversi comportamenti si può aspettare: tutto lavoro, avvertimento per la conversione tra puntatore e intero, la mancata compilazione, .. .ptr + NULLptrNULLptr + NULL(int)ptr + NULLptr + (int)NULLptrNULL


Ho visto #define NULL (void *)0prima Sei sicuro che NULL e plain 0 equivalgono al 100%?
machine_1

2
Nel contesto del puntatore, sì ... condizione enfatizzata nella mia risposta, grazie
pmg

@phuclv: non ho assolutamente idea del C ++. La mia risposta (tranne la parentesi tra parentesi) è circa C
pmg

@phuclv ptr + NULLnon viene utilizzato NULLnel contesto dei puntatori
pmg

3
@JesperJuhl: nel contesto dei puntatori sono equivalenti al 100%. Non ho idea di cosa nullptrsia, ma ((void*)0)e 0(o '\0') sono equivalenti nel contesto dei puntatori ...if (ptr == '\0' /* or equivalent 0, NULL */)
pmg

5

No, non è più preferito usare NULL(vecchio modo di inizializzazione del puntatore).

Dal C ++ 11:

La parola chiave nullptrindica il puntatore letterale. È un valore di tipo std :: nullptr_t. Esistono conversioni implicite da nullptra valore di puntatore nullo di qualsiasi tipo di puntatore e qualsiasi puntatore a tipo di membro. Esistono conversioni simili per qualsiasi costante puntatore null, che include i valori di tipo std::nullptr_te la macro NULL.

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

In realtà, std :: nullptr_t è il tipo di puntatore nullo letterale nullptr. È un tipo distinto che non è esso stesso un tipo di puntatore o un puntatore al tipo di membro.

#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)
}

Produzione:

Pointer to integer overload
Pointer to double overload
null pointer overload

La domanda riguarda l'assegnazione di NULL a 0 per numeri interi. In questo senso, nulla cambia con nullptr invece di NULL.
ivan.ukr il

Ha usato le parole come "puntatore NULL".
Mannoj,

A proposito, C ++ non ha il concetto NULL dopo C ++ 11. L'autore potrebbe essere confuso sull'uso di constexpr o definire l'inizializzazione alla vecchia maniera. en.cppreference.com/w/cpp/language/default_initialization
Mannoj
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.