Em uma revisão de código recente, um colaborador está tentando impor que todas as NULL
verificações nos ponteiros sejam executadas da seguinte maneira:
int * some_ptr;
// ...
if (some_ptr == NULL)
{
// Handle null-pointer error
}
else
{
// Proceed
}
ao invés de
int * some_ptr;
// ...
if (some_ptr)
{
// Proceed
}
else
{
// Handle null-pointer error
}
Concordo que o caminho dele é um pouco mais claro no sentido de que está dizendo explicitamente "Certifique-se de que este ponteiro não seja NULL", mas eu diria que, dizendo que qualquer pessoa que esteja trabalhando nesse código entenderá que usar uma variável de ponteiro em um if
está implicitamente procurando NULL
. Também acho que o segundo método tem uma chance menor de introduzir um bug do ilk:
if (some_ptr = NULL)
que é apenas uma dor absoluta para encontrar e depurar.
Qual o caminho que você prefere e por quê?
c++
c
if-statement
null
coding-style
Bryan Marble
fonte
fonte
Respostas:
Na minha experiência, os testes do formulário
if (ptr)
ouif (!ptr)
são preferidos. Eles não dependem da definição do símboloNULL
. Eles não expõem a oportunidade para a atribuição acidental. E eles são claros e sucintos.Edit: Como o SoapBox aponta em um comentário, eles são compatíveis com classes C ++, como
auto_ptr
objetos que atuam como ponteiros e que fornecem uma conversãobool
para permitir exatamente esse idioma. Para esses objetos, uma comparação explícitaNULL
teria que invocar uma conversão em ponteiro, que pode ter outros efeitos colaterais semânticos ou ser mais cara do que a simples verificação de existência que abool
conversão implica.Eu tenho uma preferência por código que diga o que significa sem texto desnecessário.
if (ptr != NULL)
tem o mesmo significado,if (ptr)
mas à custa de especificidade redundante. A próxima coisa lógica é escreverif ((ptr != NULL) == TRUE)
e, dessa maneira, está a loucura. A linguagem C é clara que um booleano testado porif
,while
ou similar, tem um significado específico de valor diferente de zero é verdadeiro e zero é falso. A redundância não a torna mais clara.fonte
int y = *x; if (!x) {...}
que o otimizador pode raciocinar que, uma vez que*x
seria indefinido sex
for NULL, ele não deve ser NULL e, portanto,!x
deve ser FALSE. A expressão não!ptr
é um comportamento indefinido. No exemplo, se o teste fosse feito antes de qualquer uso*x
, o otimizador estaria errado ao eliminar o teste.if (foo)
é claro o suficiente. Use-o.fonte
Vou começar com isso: a consistência é o rei, a decisão é menos importante do que a consistência em sua base de código.
Em C ++
NULL é definido como 0 ou 0L em C ++.
Se você leu A linguagem de programação C ++ que Bjarne Stroustrup sugere usar
0
explicitamente para evitar aNULL
macro ao realizar tarefas , não tenho certeza se ele fez o mesmo com as comparações, já faz um tempo desde que li o livro, acho que ele fezif(some_ptr)
sem uma comparação explícita, mas sou confuso nisso.A razão para isso é que a
NULL
macro é enganosa (como quase todas as macros). Na verdade0
, é literal, não um tipo exclusivo, como o nome sugere. Evitar macros é uma das diretrizes gerais em C ++. Por outro lado,0
parece um número inteiro e não é quando comparado ou atribuído a ponteiros. Pessoalmente, eu poderia ir de qualquer maneira, mas normalmente pulo a comparação explícita (embora algumas pessoas não gostem disso, provavelmente é por isso que você tem um colaborador sugerindo uma alteração).Independentemente dos sentimentos pessoais, essa é basicamente uma escolha do menos mau, pois não existe um método correto.
Isso é claro e é um idioma comum e eu prefiro, não há chance de atribuir um valor acidentalmente durante a comparação e ele lê claramente:
Isso fica claro se você souber que
some_ptr
é um tipo de ponteiro, mas também pode parecer uma comparação inteira:Isso é claro, em casos comuns, faz sentido ... Mas é uma abstração com vazamento,
NULL
na verdade é0
literal e pode acabar sendo mal utilizada facilmente:O C ++ 0x possui nullptr, que agora é o método preferido, pois é explícito e preciso, mas tenha cuidado com a atribuição acidental:
Até que você possa migrar para o C ++ 0x, eu diria que é uma perda de tempo se preocupar com qual desses métodos você usa, todos eles são insuficientes e é por isso que o nullptr foi inventado (junto com problemas genéricos de programação que surgiram com o encaminhamento perfeito .) O mais importante é manter a consistência.
Em C
C é um animal diferente.
Em C NULL pode ser definido como 0 ou como ((vazio *) 0), C99 permite constantes de ponteiro nulo definidas de implementação. Portanto, na verdade, tudo se resume à definição de NULL da implementação e você terá que inspecioná-la na sua biblioteca padrão.
As macros são muito comuns e, em geral, são muito usadas para compensar deficiências no suporte genérico à programação na linguagem e em outras coisas. A linguagem é muito mais simples e a dependência do pré-processador é mais comum.
Nessa perspectiva, eu provavelmente recomendaria o uso da
NULL
definição de macro em C.fonte
0
em C ++, que tem significado em C:(void *)0
.0
é preferívelNULL
em C ++, porque erros de tipo podem ser uma PITA.NULL
é zero de qualquer maneira.#define NULL ((void *)0)
delinux/stddef.h
NULL
deve ser zero.Eu uso
if (ptr)
, mas não vale a pena discutir isso completamente.Eu gosto do meu jeito porque é conciso, embora outros digam
== NULL
facilite a leitura e seja mais explícito. Vejo de onde eles vêm, apenas discordo das coisas extras que facilitam ainda mais. (Eu odeio a macro, então sou tendenciosa.) Depende de você.Eu discordo do seu argumento. Se você não receber avisos de atribuições de forma condicional, precisará aumentar seus níveis de aviso. Simples assim. (E pelo amor de tudo o que é bom, não os troque.)
Observe que em C ++ 0x, podemos fazer o
if (ptr == nullptr)
que, para mim, é melhor. (Mais uma vez, eu odeio a macro. Masnullptr
é legal.) Mas ainda o façoif (ptr)
, apenas porque é o que estou acostumado.fonte
Francamente, não vejo por que isso importa. Qualquer um deles é bastante claro e qualquer pessoa com experiência moderada em C ou C ++ deve entender os dois. Um comentário, no entanto:
Se você planeja reconhecer o erro e não continuar executando a função (ou seja, você lançará uma exceção ou retornará um código de erro imediatamente), faça disso uma cláusula de guarda:
Dessa forma, você evita "código de seta".
fonte
if(!p)
é um comportamento indefinido. Poderia funcionar ou não.if(!p)
comportamento não é indefinido, é a linha anterior a ele. Eif(p==NULL)
teria exatamente o mesmo problema.Pessoalmente, sempre usei
if (ptr == NULL)
porque explicita minha intenção, mas neste momento é apenas um hábito.O uso
=
no lugar de==
será capturado por qualquer compilador competente com as configurações de aviso corretas.O ponto importante é escolher um estilo consistente para o seu grupo e cumpri-lo. Não importa para onde você vá, você acabará se acostumando, e a perda de atrito ao trabalhar no código de outras pessoas será bem-vinda.
fonte
Apenas mais um ponto a favor da
foo == NULL
prática: sefoo
for, digamos, umint *
ou abool *
, oif (foo)
cheque pode ser acidentalmente interpretado por um leitor como testando o valor do apontador, ou seja, comoif (*foo)
. ANULL
comparação aqui é um lembrete de que estamos falando de um ponteiro.Mas suponho que uma boa convenção de nomenclatura torne esse argumento discutível.
fonte
Na verdade, eu uso as duas variantes.
Existem situações em que você primeiro verifica a validade de um ponteiro e, se for NULL, basta retornar / sair de uma função. (Eu sei que isso pode levar à discussão "uma função deve ter apenas um ponto de saída")
Na maioria das vezes, você verifica o ponteiro, faz o que deseja e resolve o caso de erro. O resultado pode ser o código recuado x-vezes feio com vários if's.
fonte
goto
pode ser muito útil. Também em muitos casos, ummalloc()
que precisaria de limpeza poderia ser substituído por um mais rápidoalloca()
. Eu sei que eles não são recomendados, mas existem por uma razão (por mim, uma etiqueta bem nomeadagoto
é muito mais limpa do que umaif/else
árvore ofuscada ).alloca
é fora do padrão e completamente não seguro. Se falhar, seu programa simplesmente explode. Não há chance de recuperação e, como a pilha é relativamente pequena, é provável que haja falha. Em caso de falha, é possível que você esclareça a pilha e introduza vulnerabilidades comprometedoras de privilégios. Nunca usealloca
ou vlas, a menos que você tenha um pequeno limite no tamanho que será alocado (e, em seguida, é melhor usar apenas uma matriz normal).A linguagem de programação C (K&R) solicita que você verifique nulo == ptr para evitar uma atribuição acidental.
fonte
if (!valid)
acimaif (valid == 0
).Se o estilo e o formato fizerem parte de suas revisões, deve haver um guia de estilo acordado para avaliar. Se houver, faça o que o guia de estilo diz. Se não houver um, detalhes como este devem ser deixados à medida que são escritos. É um desperdício de tempo e energia, e distrai o que as revisões de código realmente deveriam estar descobrindo. Sério, sem um guia de estilo, eu pressionaria para NÃO alterar um código como esse por uma questão de princípio, mesmo quando ele não usar a convenção de minha preferência.
E não que isso importe, mas minha preferência pessoal é
if (ptr)
. O significado é mais imediatamente óbvio para mim do queif (ptr == NULL)
.Talvez ele esteja tentando dizer que é melhor lidar com condições de erro antes do caminho feliz? Nesse caso, ainda não concordo com o revisor. Não sei se existe uma convenção aceita para isso, mas, na minha opinião, a condição mais "normal" deve vir em primeiro lugar em qualquer declaração if. Dessa forma, tenho menos trabalho para descobrir o que é a função e como ela funciona.
A exceção a isso é se o erro fizer com que eu saia da função ou eu possa me recuperar dela antes de prosseguir. Nesses casos, eu lido com o erro primeiro:
Ao lidar com a condição incomum na frente, posso cuidar dela e depois esquecê-la. Mas se eu não conseguir voltar ao caminho feliz lidando com isso de frente, ele deve ser tratado depois o caso principal, pois torna o código mais compreensível. Na minha opinião.
Mas se não estiver em um guia de estilo, é apenas a minha opinião, e sua opinião é igualmente válida. Padronize ou não. Não deixe um revisor pseudo-padronizar apenas porque ele tem uma opinião.
fonte
Este é um dos fundamentos das duas linguagens que os ponteiros avaliam para um tipo e valor que pode ser usado como expressão de controle,
bool
em C ++ eint
em C. Basta usá-lo.fonte
if (foo = bar)
acidentalmente.Por isso prefiro
ou
No entanto, se eu estivesse escrevendo um conjunto de diretrizes de estilo, não colocaria coisas assim, colocaria coisas como:
fonte
Sou um grande fã do fato de que o C / C ++ não verifica tipos nas condições booleanas em
if
,for
ewhile
declarações. Eu sempre uso o seguinte:mesmo em números inteiros ou outro tipo que converte em bool:
Codificar neste estilo é mais legível e mais claro para mim. Apenas minha opinião pessoal.
Recentemente, enquanto trabalhava no microcontrolador OKI 431, observei o seguinte:
é mais eficiente que
porque, posteriormente, o compilador precisará comparar o valor de chx a 1. Onde chx é apenas um sinalizador verdadeiro / falso.
fonte
A maioria dos compiladores que usei avisará pelo menos a
if
atribuição sem mais açúcar de sintaxe, portanto, não compro esse argumento. Dito isto, usei tanto profissionalmente como também não tenho preferência. A== NULL
é definitivamente mais claro que na minha opinião.fonte