Posso usar if (ponteiro) em vez de if (ponteiro! = NULL)?

171

É seguro verificar se um ponteiro não está NULLescrevendo de forma simples if(pointer)ou preciso usar if(pointer != NULL)?

danijar
fonte
13
A verdade é que, se você usar uma verificação explícita, é igualmente eficaz - e geralmente preferido - testar contra 0ou nullptr. ( NULLÉ um C'ism, e requer incluindo um arquivo de cabeçalho.)
Chao
5
@danijar Você poderia usar o nullptr no C ++ moderno.
SurvivalMachine
9
@cHao Onde está o ponto em "visando a compatibilidade com C"?
Qdii
5
@danijar: Sim, você não deve usar NULLC ++ a partir daqui porque NULLé uma macro dependente de implementação que pode fornecer comportamentos ambíguos.
Alok Salvar
3
Embora este não é o caso 'se', veja esta demonstração ao vivo ideone a respeito de porque você deve evitar "NULL" e "0" para ponteiros em C ++: ideone.com/tbvXNs
kfsone

Respostas:

197

Você pode; o ponteiro nulo é implicitamente convertido em booleano false, enquanto ponteiros não nulos são convertidos em true. No padrão C ++ 11, seção Conversões Booleanas:

Um pré-valor de aritmética, enumeração sem escopo, ponteiro ou ponteiro para o tipo de membro pode ser convertido em um pré-valor do tipo bool. Um valor zero, valor de ponteiro nulo ou valor de ponteiro de membro nulo é convertido em false; qualquer outro valor é convertido em true . Um pré-valor do tipo std::nullptr_t pode ser convertido em um pré-valor do tipo bool ; o valor resultante é false .

Joni
fonte
42

Sim você pode.

  • Um ponteiro nulo é convertido em falso implicitamente
  • um ponteiro não nulo é convertido em true.

Isso faz parte da conversão padrão do C ++, que se enquadra na cláusula de conversão booleana :

§ 4.12 Conversões booleanas

Um pré-valor de aritmética, enumeração sem escopo, ponteiro ou ponteiro para o tipo de membro pode ser convertido em um pré-valor do tipo bool. Um valor zero, um valor de ponteiro nulo ou um valor de ponteiro de membro nulo é convertido em falso; qualquer outro valor é convertido em true. Um pré-valor do tipo std :: nullptr_t pode ser convertido em um pré-valor do tipo bool; o valor resultante é falso.

billz
fonte
29

Sim você pode. Na verdade, eu prefiro usar if(pointer)porque é mais fácil ler e escrever quando você se acostumar.

Observe também que o C ++ 11 introduziu o nullptrque é preferível NULL.

Yu Hao
fonte
10
Um ponteiro não é uma expressão booleana. É convertido implicitamente. Se é melhor ler quando você precisa se lembrar dessa conversão para entender, é a sua opinião. É apenas um tipo de estilo de codificação.
harper
7
@ harper Você pode dizer que é um estilo de codificação. Mas você pode aplicar a mesma lógica ao if(som_integer)vs if(some_integer != 0)porque números inteiros também não são booleanos, certo? Eu prefiro evitar 0ou NULLem uma declaração if.
Yu Hao
13
Concordo que é simplesmente uma questão de estilo de codificação. Eu vim a if (pointer)me preferir , mas me if (ptr != nullptr)parece perfeitamente legítimo. Por outro lado, se eu visse alguém da minha equipe que escrevesse, if (some_integer)eu os faria mudar para if (some_integer != 0). No entanto, não vou fingir que não é uma preferência relativamente arbitrária da minha parte - simplesmente prefiro não tratar ponteiros e números inteiros da mesma forma.
Joel
1
@YuHao E já que é estilo de código, eu não indicaria "é preferido", mas "eu prefiro".
harper
5
@ franji1 Então, que tal if(isReady) if(filePtr) if(serviceNo)? Criar nomes de variáveis ​​incorretos de propósito não significa muito neste caso. Enfim, eu já entendi seu ponto de vista e entendi, mas posso insistir em usar meu próprio estilo de codificação, ok?
Yu Hao
13

A pergunta foi respondida, mas eu gostaria de acrescentar meus pontos.

Eu vou sempre preferir if(pointer), em vez de if(pointer != NULL)e if(!pointer)em vez de if(pointer == NULL):

  • É simples, pequeno
  • Menos chances de escrever um código de buggy, que se eu grafada operador cheque igualdade ==com =
    if(pointer == NULL)pode ser grafada if(pointer = NULL)Então eu vou evitá-lo, o melhor é apenas if(pointer).
    (Eu também sugeri alguma condição Yoda em uma resposta , mas isso é uma questão diferente)

  • Da mesma forma while (node != NULL && node->data == key), escreverei o while (node && node->data == key)que é mais óbvio para mim (mostra que usando curto-circuito).

  • (pode ser uma razão estúpida) Como NULL é uma macro, suponha que alguém redefina por engano com outro valor.
Grijesh Chauhan
fonte
6
Usando = em vez de == quase sempre gera um aviso do compilador, nos dias em que não as pessoas usariam if (NULL == PTR)
paulm
@paulm que acabei de adicionar este ponto é chamado de Condição Yoda, algumas pessoas não gostam disso como menos legível.
Grijesh Chauhan
2
(boolean expression)? true : falseé completamente inútil. A expressão avalia para trueou para false; o que você diz é "se é verdade, me dê verdadeira, se for falsa, me dê falsa". Resumindo: é completamente equivalente à própria expressão booleana. Observe que node == NULLé uma expressão booleana. BTW, suas duas implementações retornam exatamente o oposto uma da outra. Ou você quer !=no primeiro, ou apenas um !no segundo.
Celtschk
BTW, uma proteção possível contra, em =vez de, ==é fazer suas variáveis constsempre que possível. Por exemplo, você pode definir sua função como isEmnpy(node* const head) { ... }e, em seguida, o compilador se recusaria a compilá-la se você escrevesse acidentalmente em node = NULLvez de node == NULL. Claro que isso funciona apenas para variáveis ​​que você realmente não precisa alterar.
Celtschk
2
Porque as classes de ponteiros inteligentes têm, em T* get() constvez de operator T*() constevitar conversões implícitas. No entanto, eles têm um operator bool() const.
StellarVortex
13

A verificação explícita de NULL pode fornecer uma dica ao compilador sobre o que você está tentando fazer, o que leva a ser menos propenso a erros.

insira a descrição da imagem aqui

Minqi Pan
fonte
8

Sim você pode. A capacidade de comparar valores com zeros implicitamente foi herdada de C e existe em todas as versões do C ++. Você também pode usar if (!pointer)para verificar os ponteiros quanto a NULL.

dasblinkenlight
fonte
2

Os casos de uso relevantes para ponteiros nulos são

  • Redirecionamento para algo como um nó de árvore mais profundo, que pode não existir ou ainda não foi vinculado. Isso é algo que você deve sempre manter estreitamente encapsulado em uma classe dedicada, para que a legibilidade ou a concisão não sejam um problema tão grande aqui.
  • Moldes dinâmicos. A conversão de um ponteiro de classe base para um de classe derivada específico (algo que você deve tentar novamente evitar, mas às vezes pode achar necessário) sempre é bem-sucedida, mas resulta em um ponteiro nulo se a classe derivada não corresponder. Uma maneira de verificar isso é

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

    (ou, de preferência, auto derived_ptr = ...). Agora, isso é ruim, porque deixa o ponteiro derivado (possivelmente inválido, ou seja, nulo) fora ifdo escopo do bloco de proteção . Isso não é necessário, pois o C ++ permite introduzir variáveis ​​conversíveis em booleano dentro de uma ifcondição- :

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

    que não é apenas mais curto e seguro para o escopo, também é muito mais claro em sua intenção: quando você verifica nulo em uma condição if separada, o leitor se pergunta "ok, então derived_ptrnão deve ser nulo aqui ... bem, por que ser nulo? " Considerando que a versão de uma linha diz muito claramente "se você pode transmitir base_ptrcom segurança Derived*, então use-o para ...".

    O mesmo funciona da mesma forma para qualquer outra operação de falha possível que retorna um ponteiro, embora o IMO geralmente evite isso: é melhor usar algo como boost::optionalo "contêiner" para resultados de possíveis operações com falha, em vez de ponteiros.

Assim, se o caso de uso principal para ponteiros nulos deve sempre ser escrito em uma variação do-cast-style implícita, eu diria que é bom por razões de coerência para sempre usar este estilo, ou seja, eu defendo para if(ptr)cima if(ptr!=nullptr).


Receio ter que terminar com um anúncio: a if(auto bla = ...)sintaxe é, na verdade, apenas uma aproximação um pouco complicada da solução real para esses problemas: correspondência de padrões . Por que você primeiro força alguma ação (como lançar um ponteiro) e depois considera que pode haver uma falha ... Quero dizer, é ridículo, não é? É como, você tem alguns alimentos e quer fazer sopa. Você o entrega ao assistente com a tarefa de extrair o suco, se for um vegetal macio. Você não olha primeiro para isso. Quando você tem uma batata, ainda a entrega ao seu assistente, mas ele a devolve ao seu rosto com uma nota de falha. Ah, programação imperativa!

Muito melhor: considere imediatamente todos os casos que você pode encontrar. Então aja de acordo. 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 também tem ferramentas especiais para quando há realmente uma possibilidade séria de falha (assim como para várias outras coisas): mônadas. Mas este não é o lugar para explicar isso.

⟨/advertir⟩

leftaroundabout
fonte
1
Só consigo ver uma frase nessa mesa interminável que realmente responde à pergunta.
Marquês de Lorne
@ EPJ: se você pegar a pergunta literalmente (" posso usar"), ela não será respondida explicitamente (a resposta é simplesmente "sim"). Tentei apresentar as razões pelas quais o OP deveria de fato usar, if(ptr)e não if(ptr != nullptr), as quais há muito mais a dizer.
leftaroundabout
1

sim, claro! de fato, escrever se (ponteiro) é uma maneira mais conveniente de escrever do que se (ponteiro! = NULL) porque: 1. é fácil depurar 2. fácil entender 3. se, acidentalmente, o valor de NULL for definido, então também o código não trava

Palak Jain
fonte
0

Sim. Na verdade você deveria. Se você está se perguntando se isso cria uma falha de segmentação , isso não ocorre.

darshandzend
fonte
29
Por que você deveria?
Qdii
0

Como outros já responderam bem, ambos são intercambiáveis.

No entanto, vale ressaltar que pode haver um caso em que você queira usar a declaração explícita, ie pointer != NULL.

Consulte também https://stackoverflow.com/a/60891279/2463963

z3moon
fonte
-1

Sim, você sempre pode fazer isso como a condição 'SE' avalia apenas quando a condição interna se torna verdadeira. C não possui um tipo de retorno booleano e, portanto, retorna um valor diferente de zero quando a condição é verdadeira, enquanto retorna 0 sempre que a condição em 'IF' for falsa. O valor diferente de zero retornado por padrão é 1. Portanto, as duas maneiras de escrever o código estão corretas, enquanto eu sempre preferirei o segundo.

user2280507
fonte
1
O valor diferente de zero, por padrão, é indefinido, se bem me lembro.
Marquês de Lorne
-1

Eu acho que, como regra geral, se sua expressão if puder ser reescrita como

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

de forma que não cause AVISOS, esse deve ser o estilo preferido para a expressão if . (Eu sei que recebo avisos quando atribuo um C BOOL( #define BOOL int) antigo a um C ++ bool, sem falar em indicadores.)

franji1
fonte
-1

"É seguro..?" é uma pergunta sobre o padrão de idioma e o código gerado.

"É uma boa prática?" é uma pergunta sobre quão bem a declaração é entendida por qualquer leitor humano arbitrário da declaração. Se você está fazendo essa pergunta, sugere que a versão "segura" é menos clara para futuros leitores e escritores.

Fred Mitchell
fonte
Minha intenção era perguntar se é seguro. Portanto, eu usei essa redação. No entanto, o que você escreveu aqui não é uma resposta para a pergunta. Em vez disso, deve ser um comentário sob a pergunta. Você pode excluir a resposta e adicionar um comentário à pergunta.
31513 danijar
@danijar Você não se lembra de quando era novo no StackOverflow e pesquisou a seção 'Comentar' sem sucesso? Alguém com 7 reputação não pode fazer isso.
24513 Broxzier
@ JimBalter O que é muito confuso, pois você pode ver outras pessoas fazendo isso. Quando eu era novo na SO, alguém me culpava por fazer isso.
Broxzier 26/08/13
@ JimBalter Eu não estou matando e roubando. Eu estava dizendo ao danijar que Fred Mitchell era um novo usuário e não podia postar comentários.
Broxzier
@ JimBalter Que você começou hoje. Também é você quem não entende. Esse comentário está apenas apoiando a confusão disso.
precisa saber é o seguinte