Posso usare if (pointer) invece di if (pointer! = NULL)?


171

È sicuro controllare un puntatore al non essere NULLscrivendo semplicemente if(pointer)o devo usare if(pointer != NULL)?


13
La verità è che, se hai intenzione di usare un controllo esplicito, è altrettanto efficace - e spesso preferito - testare contro 0o nullptr. ( NULLè un C'ism e richiede l'inclusione di un file di intestazione.)
cHao,

5
@danijar Potresti usare nullptr nel moderno C ++.
SurvivalMachine,

9
@cHao Dov'è il punto "puntare alla compatibilità con C"?
qdii

5
@danijar: Sì, non dovresti usare NULLin C ++ da qui in poi perché la NULLmacro dipende dall'implementazione che potrebbe darti comportamenti ambigui.
Risparmia il

3
Anche se questo non è il caso 'if', vedi questa demo live di ideone sul perché dovresti evitare "NULL" e "0" per i puntatori in C ++: ideone.com/tbvXNs
kfsone

Risposte:


198

Puoi; il puntatore null viene implicitamente convertito in booleano false mentre i puntatori non null vengono convertiti in true. Dallo standard C ++ 11, sezione sulle conversioni booleane:

Un valore di aritmetica, enumerazione senza ambito, puntatore o puntatore al tipo di membro può essere convertito in un valore di tipo bool. Viene convertito un valore zero, un valore del puntatore nullo o un valore del puntatore del membro null false; qualsiasi altro valore viene convertito in true . Un valore di tipo std::nullptr_t può essere convertito in un valore di tipo bool ; il valore risultante è false .


42

Sì, potresti.

  • Un puntatore null viene convertito in false implicitamente
  • un puntatore non nullo viene convertito in vero.

Questo fa parte della conversione standard C ++, che rientra nella clausola di conversione booleana :

§ 4.12 Conversioni booleane

Un valore di aritmetica, enumerazione senza ambito, puntatore o puntatore al tipo di membro può essere convertito in un valore di tipo bool. Un valore zero, un valore del puntatore nullo o un valore del puntatore del membro null viene convertito in false; qualsiasi altro valore viene convertito in vero. Un valore di tipo std :: nullptr_t può essere convertito in un valore di tipo bool; il valore risultante è falso.


29

Si, puoi. In effetti, preferisco usare if(pointer)perché è più semplice leggere e scrivere una volta che ti ci abitui.

Si noti inoltre che è stato introdotto C ++ 11 nullptrpreferito rispetto a NULL.


10
Un puntatore non è un'espressione booleana. Viene convertito implicitamente. Se è meglio leggere quando devi ricordare questa conversione per capire è la tua opinione. È solo un tipo di stile di codifica.
Harper il

7
@harper Puoi dire che è uno stile di codifica. Ma puoi applicare la stessa logica a if(som_integer)vs if(some_integer != 0)perché anche gli interi non sono booleani, giusto? Preferisco evitare 0o NULLin una dichiarazione if.
Yu Hao,

13
Sono d'accordo che è semplicemente una questione di stile di codifica. Sono venuto a preferire if (pointer)me stesso, ma mi if (ptr != nullptr)sembra perfettamente legittimo. D'altra parte, se vedessi qualcuno nella mia squadra che ha scritto, if (some_integer)lo farei cambiare if (some_integer != 0). Tuttavia, non pretendo che non sia una preferenza relativamente arbitraria da parte mia: semplicemente preferisco non trattare i puntatori e gli interi allo stesso modo.
Gioele,

1
@YuHao E dato che è in stile codice non direi "è preferito" ma "preferisco".
Harper

5
@ franji1 Allora che ne dici if(isReady) if(filePtr) if(serviceNo)? Fare intenzionalmente nomi di variabili errati non significa molto in questo caso. Ad ogni modo ho già capito il tuo punto e l'ho capito, ma posso insistere usando il mio stile di codifica, ok?
Yu Hao,

13

Risposte alla domanda, ma vorrei aggiungere i miei punti.

Preferirò sempre if(pointer)anziché if(pointer != NULL)e if(!pointer)invece di if(pointer == NULL):

  • È semplice, piccolo
  • Meno possibilità di scrivere un codice errato, supponiamo che se ho sbagliato a scrivere l'operatore di controllo dell'uguaglianza ==con =
    if(pointer == NULL)possa essere errato if(pointer = NULL)Quindi lo eviterò, il migliore è giusto if(pointer).
    (Ho anche suggerito alcune condizioni di Yoda in una risposta , ma questa è una questione diversa)

  • Allo stesso modo while (node != NULL && node->data == key), scriverò semplicemente while (node && node->data == key)che è più ovvio per me (mostra che usare il corto circuito).

  • (può essere una ragione stupida) Poiché NULL è una macro, se supponiamo che qualcuno lo ridefinisca per errore con un altro valore.

6
L'uso di = anziché == genera quasi sempre un avviso del compilatore, nei giorni in cui non veniva utilizzato da persone (NULL == ptr)
paulm

@paulm che ho appena aggiunto questo punto si chiama Yoda Condition ad alcune persone non piace perché è meno leggibile.
Grijesh Chauhan,

2
(boolean expression)? true : falseè completamente inutile. L'espressione valuta trueo to false; quello che dici è "se è vero, dammi vero, se è falso, dammi falso". In breve: è completamente equivalente all'espressione booleana stessa. Nota che node == NULLè un'espressione booleana. A proposito, le tue due implementazioni restituiscono esattamente il contrario l'una dell'altra. O vuoi !=nel primo o solo uno !nel secondo.
Celtschk,

A proposito, una possibile protezione contro =invece di ==è rendere le variabili constogni volta che è possibile. Ad esempio, è possibile definire la funzione come isEmnpy(node* const head) { ... }, quindi il compilatore rifiuta di compilarla se si scrivesse accidentalmente node = NULLanziché node == NULL. Ovviamente funziona solo con variabili che non è necessario modificare.
Celtschk,

2
Perché le classi di puntatori intelligenti hanno T* get() constinvece di operator T*() constevitare conversioni implicite. Hanno comunque un operator bool() const.
StellarVortex,

13

Il controllo esplicito di NULL potrebbe fornire un suggerimento al compilatore su ciò che si sta tentando di fare, ergo portando ad essere meno soggetto a errori.

inserisci qui la descrizione dell'immagine


8

Si, puoi. La possibilità di confrontare implicitamente i valori con gli zeri è stata ereditata da C ed è presente in tutte le versioni di C ++. Puoi anche usare if (!pointer)per controllare i puntatori per NULL.


2

I casi d'uso rilevanti per i puntatori null sono

  • Reindirizzamento a qualcosa come un nodo ad albero più profondo, che potrebbe non esistere o che non è stato ancora collegato. È qualcosa che dovresti sempre tenere strettamente incapsulato in una classe dedicata, quindi la leggibilità o la concisione non sono un grosso problema qui.
  • Calchi dinamici. Lanciare un puntatore di classe base su un particolare di classe derivata (qualcosa che dovresti ancora tentare di evitare, ma a volte potresti ritenere necessario) ha sempre successo, ma si traduce in un puntatore nullo se la classe derivata non corrisponde. Un modo per verificarlo è

    Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
    if(derived_ptr != nullptr) { ... }

    (o, preferibilmente, auto derived_ptr = ...). Ora, questo è negativo, perché lascia il puntatore derivato (possibilmente non valido, cioè nullo) al di fuori ifdell'ambito del blocco di protezione di sicurezza . Questo non è necessario, come C ++ consente di introdurre variabili booleane-Convertable all'interno di un ifCircostanza :

    if(auto derived_ptr = dynamic_cast<Derived*>(base_ptr)) { ... }

    che non è solo più breve e sicuro per l'ambito, è anche molto più chiaro nella sua intenzione: quando si verifica la presenza di null in una condizione if separata, il lettore si chiede "ok, quindi derived_ptrnon deve essere null qui ... beh, perché dovrebbe essere nullo? " Mentre la versione di una sola riga dice molto chiaramente: "Se si può tranquillamente lanciare base_ptra Derived*, quindi usarlo per ...".

    Lo stesso vale anche per qualsiasi altra operazione di errore possibile che restituisce un puntatore, anche se IMO dovresti generalmente evitarlo: è meglio usare qualcosa come boost::optionalil "contenitore" per risultati di possibili operazioni fallite, piuttosto che puntatori.

Quindi, se il caso d'uso principale per i puntatori null dovesse sempre essere scritto in una variante dello stile di cast implicito, direi che è buono per motivi di coerenza usare sempre questo stile, vale a dire difenderei per if(ptr)over if(ptr!=nullptr).


Temo di dover finire con un annuncio: la if(auto bla = ...)sintassi è in realtà solo un'approssimazione leggermente ingombrante alla vera soluzione a tali problemi: il pattern matching . Perché prima dovresti forzare qualche azione (come lanciare un puntatore) e poi considerare che potrebbe esserci un fallimento ... Voglio dire, è ridicolo, non è vero? È come se avessi del cibo e vuoi fare una zuppa. Lo passi al tuo assistente con il compito di estrarre il succo, se capita di essere una verdura morbida. Prima non lo guardi. Quando hai una patata, la dai ancora al tuo assistente, ma te la schiaffeggiano in faccia con una nota di fallimento. Ah, programmazione imperativa!

Molto meglio: considera subito tutti i casi che potresti incontrare. Quindi agire di conseguenza. Haskell:

makeSoupOf :: Foodstuff -> Liquid
makeSoupOf p@(Potato{..}) = mash (boil p) <> water
makeSoupOf vegetable
 | isSoft vegetable  = squeeze vegetable <> salt
makeSoupOf stuff  = boil (throwIn (water<>salt) stuff)

Haskell ha anche strumenti speciali per quando c'è davvero una seria possibilità di fallimento (così come per un sacco di altre cose): le monadi. Ma questo non è il posto giusto per spiegarli.

⟨/ Advert⟩


1
Vedo solo una frase in questo interminabile massetto che risponde effettivamente alla domanda.
Marchese di Lorne,

@EJP: se prendi la domanda alla lettera (" posso usare"), allora non ha una risposta esplicita (la risposta è semplicemente "sì"). Ho cercato di fornire le giuste ragioni per cui l'OP dovrebbe effettivamente utilizzare if(ptr)piuttosto che if(ptr != nullptr), a cui c'è ancora molto da dire.
lasciato circa il

1

sì, naturalmente! in effetti, scrivere if (pointer) è un modo più conveniente di scrivere piuttosto che if (pointer! = NULL) perché: 1. è facile eseguire il debug 2. facile da capire 3. se per caso, viene definito il valore di NULL, quindi anche il codice non andrà in crash



0

Come altri hanno già risposto bene, entrambi sono intercambiabili.

Tuttavia, vale la pena ricordare che potrebbe esserci un caso in cui potresti voler usare la dichiarazione esplicita, ad es pointer != NULL.

Vedi anche https://stackoverflow.com/a/60891279/2463963


-1

Sì, puoi sempre farlo mentre la condizione 'IF' viene valutata solo quando la condizione al suo interno diventa vera. C non ha un tipo di ritorno booleano e quindi restituisce un valore diverso da zero quando la condizione è vera mentre restituisce 0 ogni volta che la condizione in 'IF' risulta essere falsa. Il valore diverso da zero restituito per impostazione predefinita è 1. Pertanto, entrambi i modi di scrivere il codice sono corretti mentre preferirò sempre il secondo.


1
Il valore diverso da zero per impostazione predefinita non è definito se ricordo correttamente.
Marchese di Lorne,

-1

Penso come regola empirica, se la tua espressione if può essere riscritta come

const bool local_predicate = *if-expression*;
if (local_predicate) ...

tale da non causare AVVERTENZE, quindi QUELLO dovrebbe essere lo stile preferito per l' espressione if . (So ​​di ricevere avvisi quando assegno un vecchio C BOOL( #define BOOL int) a un C ++ bool, per non parlare dei puntatori.)


-1

"È sicuro..?" è una domanda sullo standard di lingua e sul codice generato.

"È una buona pratica?" è una domanda sul modo in cui l'affermazione è compresa da qualsiasi lettore umano arbitrario dell'affermazione. Se stai ponendo questa domanda, suggerisce che la versione "sicura" è meno chiara per i futuri lettori e scrittori.


La mia intenzione era di chiedere se è sicuro. Pertanto ho usato quella formulazione. Tuttavia, ciò che hai scritto qui non è una risposta alla domanda. Invece, dovrebbe essere un commento sotto la domanda. È quindi possibile eliminare la risposta e aggiungere un commento sotto la domanda.
danijar,

@danijar Non ricordi quando eri nuovo su StackOverflow e hai cercato la sezione 'Comment' senza successo? Qualcuno con 7 reputazione non può farlo.
Broxzier,

@JimBalter Che è molto confuso, dal momento che puoi vedere gli altri farlo. Quando ero nuovo di SO qualcuno mi incolpava di averlo fatto.
Broxzier,

@JimBalter Non sto assassinando e rubando. Stavo dicendo a danijar che Fred Mitchell era un nuovo utente e non poteva pubblicare commenti.
Broxzier,

@JimBalter Che hai iniziato oggi. Anche tu invece non capisci. Quel commento supporta solo la confusione di questo.
Broxzier,
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.