Come verifichiamo se un puntatore è un puntatore NULL?

Penso sempre che if(p != NULL){..} farà il lavoro. Ma dopo aver letto questa domanda di Stack Overflow , non sembra.

Quindi qual è il modo canonico per verificare i puntatori NULL dopo aver assorbito tutta la discussione in quella domanda che dice che i puntatori NULL possono avere un valore diverso da zero?

Penso sempre che se (p! = NULL) {..} farà il lavoro.

Lo farà.

Innanzitutto, per essere chiari al 100%, non c’è differenza tra C e C ++ qui. E in secondo luogo, la domanda Stack Overflow che citi non parla di puntatori nulli; introduce dei puntatori non validi; puntatori che, almeno per quanto riguarda lo standard, causano comportamenti indefiniti solo cercando di confrontarli. Non c’è modo di testare in generale se un puntatore è valido.

Alla fine, ci sono tre modi diffusi per controllare un puntatore nullo:

 if ( p != NULL ) ... if ( p != 0 ) ... if ( p ) ... 

Tutto funziona, indipendentemente dalla rappresentazione di un puntatore nullo sulla macchina. E tutti, in un modo o nell’altro, sono fuorvianti; quale sceglierai è una questione di scegliere il meno male. Formalmente, i primi due sono indentici per il compilatore; la costante NULL o 0 viene convertita in un puntatore nullo del tipo di p , ei risultati della conversione vengono confrontati con p . Indipendentemente dalla rappresentazione di un puntatore nullo.

Il terzo è leggermente diverso: p è implicitamente convertito in bool . Ma la conversione implicita è definita come il risultato di p != 0 , quindi si finisce con la stessa cosa. (Il che significa che non c’è davvero alcun argomento valido per usare il terzo stile: si offusca con una conversione implicita, senza alcun beneficio di compensazione).

Quale dei primi due che preferisci è in gran parte una questione di stile, forse parzialmente dettata dal tuo stile di programmazione altrove: a seconda dell’idioma in questione, una delle bugie sarà più fastidiosa dell’altra. Se fosse solo una questione di confronto, penso che molte persone preferirebbero NULL , ma in qualcosa come f( NULL ) , il sovraccarico che verrà scelto è f( int ) , e non un sovraccarico con un puntatore. Allo stesso modo, se f è un modello di funzione, f( NULL ) istanzia il modello su int . (Naturalmente, alcuni compilatori, come g ++, genereranno un avvertimento se NULL viene usato in un contesto non puntatore, se usi g ++, dovresti usare NULL .)

In C ++ 11 , ovviamente, l’idioma preferito è:

 if ( p != nullptr ) ... 

, che evita la maggior parte dei problemi con le altre soluzioni. (Ma non è compatibile con C :-)).

Il compilatore deve fornire un sistema di tipi coerente e fornire un insieme di conversioni standard. Né il valore intero 0 né il puntatore NULL devono essere rappresentati da bit tutto-zero, ma il compilatore deve fare attenzione a convertire il token “0” nel file di input alla rappresentazione corretta per zero intero e il tipo di puntatore al cast deve convertire dal numero intero alla rappresentazione del puntatore.

L’implicazione di questo è quello

 void *p; memset(&p, 0, sizeof p); if(p) { ... } 

non è garantito che si comportino allo stesso modo su tutti i sistemi di destinazione, dato che stai facendo un’ipotesi sul pattern di bit qui.

Ad esempio, ho una piattaforma integrata che non ha protezioni di memoria e mantiene i vettori di interrupt sull’indirizzo 0, quindi per convenzione, gli interi ei puntatori sono XORed con 0x2000000 quando convertiti, che lascia (void *) 0 puntando a un indirizzo che genera un errore di bus quando è dereferenziato, tuttavia testare il puntatore con un’istruzione if lo restituirà prima alla rappresentazione intera, che è quindi tutti zeri.

La rappresentazione reale di un puntatore nullo è irrilevante qui. Un valore intero letterale con valore zero (compreso 0 e qualsiasi definizione valida di NULL ) può essere convertito in qualsiasi tipo di puntatore, fornendo un puntatore nullo, indipendentemente dalla rappresentazione effettiva. Quindi p != NULL , p != 0 e p sono tutti test validi per un puntatore non nullo.

Potresti incontrare problemi con rappresentazioni non nette del puntatore nullo se hai scritto qualcosa di simile a p != reinterpret_cast(0) , quindi non farlo.

Anche se ho appena notato che la tua domanda è etichettata come C e C ++. La mia risposta si riferisce al C ++ e altre lingue potrebbero essere diverse. Quale lingua stai usando?

Apparentemente il thread che mi riferisci riguarda il C++ .

In C tuo frammento funzionerà sempre. Mi piace il più semplice if (p) { /* ... */ } .

La rappresentazione dei puntatori è irrilevante al confronto, poiché tutti i confronti in C hanno luogo come valori non rappresentazioni. L’unico modo per confrontare la rappresentazione sarebbe qualcosa di orribile come:

 static const char ptr_rep[sizeof ptr] = { 0 }; if (!memcmp(&ptr, ptr_rep, sizeof ptr)) ... 

Bene, questa domanda è stata posta e risposta nel lontano 2011, ma c’è nullptr in C ++ 11 . Questo è tutto ciò che sto usando al momento.

Puoi leggere altro da Stack Overflow e anche da questo articolo .