Domanda

All'inizio di C ++ quando era imbullonato sopra C, non era possibile usare NULL poiché era definito come (void*)0. Non è stato possibile assegnare NULL a nessun puntatore diverso da void*, il che lo ha reso inutile. In quei giorni, era accettato che tu avessi usato 0 (zero) per puntatori null.

Fino ad oggi, ho continuato a utilizzare zero come puntatore null, ma quelli intorno a me insistono sull'uso di NULL. Personalmente non vedo alcun vantaggio nel dare un nome (p && !q) a un valore esistente - e poiché mi piace anche testare i puntatori come valori di verità:

if (p && !q)
  do_something();

quindi l'uso di zero ha più senso (come nel caso in cui si usi <=>, non è possibile utilizzare logicamente <=> - è necessario confrontare esplicitamente con <=>, a meno che non si supponga che <=> sia zero, nel qual caso perché usa <=>).

C'è qualche motivo oggettivo per preferire lo zero a NULL (o viceversa) o è solo una preferenza personale?

Modifica: dovrei aggiungere (e intendevo dire originariamente) che con RAII ed eccezioni, uso raramente puntatori zero / NULL, ma a volte ne hai ancora bisogno.

È stato utile?

Soluzione

Ecco il punto di vista di Stroustrup su questo: Domande frequenti su stile e tecnica C ++

  

In C ++, la definizione di NULL è 0, quindi c'è solo una differenza estetica. Preferisco evitare le macro, quindi uso 0. Un altro problema con nullptr è che le persone a volte credono erroneamente che sia diverso da 0 e / o non un numero intero. Nel codice pre-standard, <=> era / è talvolta definito come qualcosa di non idoneo e quindi doveva / deve essere evitato. Oggi è meno comune.

     

Se devi nominare il puntatore null, chiamalo <=>; questo è ciò che viene chiamato in C ++ 11. Quindi, <=> sarà una parola chiave.

Detto questo, non sudare le piccole cose.

Altri suggerimenti

Ci sono alcuni argomenti (uno dei quali è relativamente recente) che credo contraddicano la posizione di Bjarne su questo.

  1. Documentazione di intenti

L'uso di NULL consente ricerche sull'uso e mette in evidenza che lo sviluppatore voleva utilizzare un puntatore int, indipendentemente dal fatto che sia stato interpretato dal compilatore come 0 o no.

  1. Il sovraccarico del puntatore e 'int' è relativamente raro

L'esempio che tutti citano è:

void foo(int*);
void foo (int);

void bar() {
  foo (NULL);  // Calls 'foo(int)'
}

Tuttavia, almeno secondo me, il problema con quanto sopra non è che stiamo usando NULL per la costante puntatore null, è che abbiamo sovraccarichi di "pippo" che accettano tipi di argomenti molto diversi. Anche il parametro deve essere std::nullptr_t, poiché qualsiasi altro tipo si tradurrà in una chiamata ambigua e genererà quindi un utile avviso del compilatore.

  1. Gli strumenti di analisi possono aiutare OGGI!

Anche in assenza di C ++ 0x, oggi sono disponibili strumenti che verificano che <=> sia utilizzato per i puntatori e che <=> sia utilizzato per i tipi integrali.

  1. C ++ 11 avrà un nuovo <=> tipo.

Questo è l'argomento più recente della tabella. Il problema di <=> e <=> viene affrontato attivamente per C ++ 0x e puoi garantire che per ogni implementazione che fornisce <=>, la prima cosa che faranno è:

#define NULL  nullptr

Per coloro che usano <=> anziché <=>, la modifica sarà un miglioramento della sicurezza del tipo con poco o nessun sforzo - semmai può anche catturare alcuni bug in cui hanno usato <=> <=>. Per chiunque usi <=> oggi .... erm ... beh, si spera abbiano una buona conoscenza delle espressioni regolari ...

Usa NULL. NULL mostra il tuo intento. Che sia 0 è un dettaglio di implementazione che non dovrebbe importare.

Ho smesso di usare NULL a favore di 0 molto tempo fa (così come la maggior parte delle altre macro). L'ho fatto non solo perché volevo evitare il più possibile le macro, ma anche perché NULL sembra essere stato utilizzato eccessivamente nel codice C e C ++. Sembra essere usato ogni volta che è necessario un valore 0, non solo per i puntatori.

Sui nuovi progetti, l'ho inserito in un'intestazione del progetto:

static const int nullptr = 0;

Ora, quando arrivano i compilatori conformi a C ++ 0x, tutto quello che devo fare è rimuovere quella riga. Un buon vantaggio di ciò è che Visual Studio riconosce già nullptr come parola chiave e la evidenzia in modo appropriato.

Uso sempre:

  • NULL per puntatori
  • '\0' per i caratteri
  • 0.0 per float e doppi

dove 0 andrebbe bene. Si tratta di segnalare l'intenzione. Detto questo, non ne sono anonimo.

    cerr << sizeof(0) << endl;
    cerr << sizeof(NULL) << endl;
    cerr << sizeof(void*) << endl;

    ============
    On a 64-bit gcc RHEL platform you get:
    4
    8
    8
    ================

La morale della storia. Dovresti usare NULL quando hai a che fare con i puntatori.

1) Dichiara il tuo intento (non farmi cercare in tutto il codice cercando di capire se una variabile è un puntatore o un tipo numerico).

2) In alcune chiamate API che prevedono argomenti variabili, useranno un puntatore NULL per indicare la fine dell'elenco degli argomenti. In questo caso, l'utilizzo di '0' anziché NULL può causare problemi. Su una piattaforma a 64 bit, la chiamata va_arg richiede un puntatore a 64 bit, ma passerai solo un numero intero a 32 bit. Mi sembra che tu faccia affidamento sugli altri 32 bit per essere azzerato per te? Ho visto alcuni compilatori (ad es. Icpc di Intel) che non sono così gentili - e questo ha comportato errori di runtime.

Se ricordo correttamente NULL è definito in modo diverso nelle intestazioni che ho usato. Per C è definito come (void *) 0 e per C ++ è definito come 0. Il codice assomigliava a:

#ifndef __cplusplus
#define NULL (void*)0
#else
#define NULL 0
#endif

Personalmente uso ancora il valore NULL per rappresentare i puntatori null, ciò rende esplicito che stai usando un puntatore anziché un tipo integrale. Sì, internamente il valore NULL è ancora 0 ma non è rappresentato come tale.

Inoltre, non faccio affidamento sulla conversione automatica di numeri interi in valori booleani, ma li confronto esplicitamente.

Ad esempio preferisco usare:

if (pointer_value != NULL || integer_value == 0)

anziché:

if (pointer_value || !integer_value)

Basti dire che questo è tutto risolto in C ++ 11 dove si può semplicemente usare nullptr invece di NULL, e anche nullptr_t che è il tipo di <=>.

Direi che la storia ha parlato e coloro che hanno discusso a favore dell'uso di 0 (zero) hanno sbagliato (incluso Bjarne Stroustrup). Gli argomenti a favore di 0 erano principalmente estetica e & Quot; preferenza personale & Quot ;.

Dopo la creazione di C ++ 11, con il suo nuovo tipo nullptr, alcuni compilatori hanno iniziato a lamentarsi (con parametri predefiniti) di passare 0 a funzioni con argomenti puntatore, perché 0 non è un puntatore.

Se il codice fosse stato scritto usando NULL, una semplice ricerca e sostituzione avrebbe potuto essere eseguita attraverso la base di codice per renderlo invece nullptr. Se sei bloccato con il codice scritto usando la scelta di 0 come puntatore, è molto più noioso aggiornarlo.

E se devi scrivere subito un nuovo codice nello standard C ++ 03 (e non puoi usare nullptr), dovresti semplicemente usare NULL. Ti renderà molto più semplice l'aggiornamento in futuro.

Di solito uso 0. Non mi piacciono le macro e non esiste alcuna garanzia che alcune intestazioni di terze parti che stai utilizzando non ridefiniscano NULL per essere qualcosa di strano.

È possibile utilizzare un oggetto nullptr come proposto da Scott Meyers e altri fino a quando C ++ non ottiene una parola chiave nullptr:

const // It is a const object...
class nullptr_t 
{
public:
    template<class T>
    operator T*() const // convertible to any type of null non-member pointer...
    { return 0; }

    template<class C, class T>
    operator T C::*() const   // or any type of null member pointer...
    { return 0; }

private:
    void operator&() const;  // Can't take address of nullptr

} nullptr = {};

Google " nullptr " per maggiori informazioni.

Una volta ho lavorato su una macchina in cui 0 era un indirizzo valido e NULL era definito come un valore ottale speciale. Su quella macchina (0! = NULL), quindi codice come

char *p;

...

if (p) { ... }

non funzionerebbe come previsto. DEVI scrivere

if (p != NULL) { ... }

Anche se credo che la maggior parte dei compilatori definisca NULL come 0 in questi giorni, ricordo ancora la lezione di quegli anni fa: NULL non è necessariamente 0.

Penso che lo standard garantisca NULL == 0, quindi puoi farlo anche tu. Preferisco NULL perché documenta le tue intenzioni.

L'uso di 0 o NULL avrà lo stesso effetto.

Tuttavia, ciò non significa che siano entrambe buone pratiche di programmazione. Dato che non vi è alcuna differenza nelle prestazioni, la scelta di un'opzione di basso livello rispetto a un'alternativa agnostica / astratta è una cattiva pratica di programmazione. Aiuta i lettori del tuo codice a comprendere il tuo processo di pensiero .

NULL, 0, 0.0, '\ 0', 0x00 e quant'altro si traducono tutti nella stessa cosa, ma sono entità logiche diverse nel tuo programma. Dovrebbero essere usati come tali. NULL è un puntatore, 0 è quantità, 0x0 è un valore i cui bit sono interessanti, ecc. Non assegneresti "\ 0" a un puntatore, sia esso compilato o meno.

So che alcune comunità incoraggiano a dimostrare una conoscenza approfondita di un ambiente rompendo i contratti ambientali. I programmatori responsabili, tuttavia, rendono il codice gestibile e mantengono tali pratiche fuori dal loro codice.

Strano, nessuno, incluso Stroustroup, l'ha menzionato. Parlando molto di standard ed estetica nessuno ha notato che è pericoloso usare 0 al posto di NULL, per esempio, nella lista di argomenti variabili sull'architettura dove sizeof(int) != sizeof(void*). Come Stroustroup, preferisco <=> per motivi estetici, ma bisogna stare attenti a non usarlo dove il suo tipo potrebbe essere ambiguo.

Cerco di evitare l'intera domanda usando i riferimenti C ++ ove possibile. Invece di

void foo(const Bar* pBar) { ... }

potresti spesso essere in grado di scrivere

void foo(const Bar& bar) { ... }

Naturalmente, questo non funziona sempre; ma i puntatori null possono essere abusati.

Sono con Stroustrup su questo :-) Dato che NULL non fa parte della lingua, preferisco usare 0.

Per lo più preferenze personali, anche se si potrebbe argomentare che NULL rende abbastanza ovvio che l'oggetto è un puntatore che attualmente non punta a nulla, ad esempio

void *ptr = &something;
/* lots o' code */
ptr = NULL; // more obvious that it's a pointer and not being used

IIRC, lo standard non richiede che NULL sia 0, quindi usando tutto ciò che è definito in < stddef.h > è probabilmente il migliore per il tuo compilatore.

Un altro aspetto dell'argomento è se si debbano usare confronti logici (cast implicito per bool) o controllo esplicito contro NULL, ma ciò dipende anche dalla leggibilità.

Preferisco usare NULL poiché chiarisce che il tuo intento è che il valore rappresenta un puntatore e non un valore aritmetico. Il fatto che sia una macro è sfortunato, ma dal momento che è così ampiamente radicato c'è poco pericolo (a meno che qualcuno non faccia qualcosa di veramente stordito). Vorrei che fosse una parola chiave dall'inizio, ma cosa puoi fare?

Detto questo, non ho alcun problema con l'uso dei puntatori come valori di verità in se stessi. Proprio come con NULL, è un linguaggio radicato.

C ++ 09 aggiungerà il costrutto nullptr che penso sia atteso da tempo.

Uso sempre 0. Non per nessun motivo reale, solo perché quando stavo imparando il C ++ per la prima volta ho letto qualcosa che mi ha raccomandato di usare 0 e l'ho sempre fatto così. In teoria potrebbe esserci un problema di confusione nella leggibilità, ma in pratica non ho mai incontrato un problema del genere in migliaia di ore-uomo e milioni di righe di codice. Come dice Stroustrup, è davvero solo un problema estetico personale fino a quando lo standard non diventa nullptr.

Qualcuno mi ha detto una volta ... Ho intenzione di ridefinire NULL a 69. Da allora non lo uso: P

Rende il tuo codice abbastanza vulnerabile.

Modifica

Non tutto nello standard è perfetto. La macro NULL è una costante puntatore null C ++ definita dall'implementazione non completamente compatibile con la macro C NULL, che oltre al tipo che nasconde implicito lo converte in uno strumento inutile e soggetto a errori.

NULL non si comporta come un puntatore null ma come un letterale O / OL.

Dimmi che il prossimo esempio non è confuso:

void foo(char *); 
void foo(int); 
foo(NULL); // calls int version instead of pointer version! 

È a causa di tutto ciò, nel nuovo standard appare std :: nullptr_t

Se non vuoi aspettare il nuovo standard e vuoi usare un nullptr, usa almeno uno decente come quello proposto da Meyers (vedi il commento di jon.h).

Beh, sostengo di non usare i puntatori 0 o NULL quando possibile.

Il loro utilizzo porterà prima o poi a errori di segmentazione nel codice. Nella mia esperienza questo, e gli indicatori in gereral sono una delle maggiori fonti di bug in C ++

inoltre, porta a " if-not-null " dichiarazioni in tutto il codice. Molto più bello se puoi fare affidamento su uno stato sempre valido.

Esiste quasi sempre un'alternativa migliore.

Impostare un puntatore su 0 non è poi così chiaro. Soprattutto se vieni in un linguaggio diverso dal C ++. Ciò include C e Javascript.

Recentemente ho eliminato del codice in questo modo:

virtual void DrawTo(BITMAP *buffer) =0;

per la pura funzione virtuale per la prima volta. Ho pensato che fosse un po 'di jiberjash magico per una settimana. Quando mi sono reso conto che stava semplicemente impostando il puntatore di funzione su un null (dato che le funzioni virtuali sono solo puntatori di funzione nella maggior parte dei casi per C ++) mi sono cacciato da solo.

virtual void DrawTo(BITMAP *buffer) =null;

sarebbe stato meno confuso di quella basterdazione senza una corretta spaziatura ai miei nuovi occhi. In realtà, mi chiedo perché il C ++ non impieghi in minuscolo <=> proprio come in minuscolo falso e vero ora.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top