Você usa NULL ou 0 (zero) para ponteiros em C ++?

192

Nos primeiros dias do C ++, quando ele era aparafusado em cima de C, não era possível usar NULL como definido (void*)0. Você não pode atribuir NULL a nenhum ponteiro além de void*, o que o tornou meio inútil. Naqueles dias, era aceito que você usasse 0(zero) para ponteiros nulos.

Até hoje, continuei usando zero como ponteiro nulo, mas aqueles que estão à minha volta insistem em usá-lo NULL. Pessoalmente, não vejo nenhum benefício em atribuir um nome ( NULL) a um valor existente - e como também gosto de testar ponteiros como valores verdadeiros:

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

então, usar zero faz mais sentido (como se você usar NULL, não é possível usar logicamente p && !q- você precisa comparar explicitamente NULL, a menos que assuma que NULLé zero; nesse caso, por que usar NULL).

Existe alguma razão objetiva para preferir zero a NULL (ou vice-versa), ou é apenas preferência pessoal?

Editar: devo adicionar (e pretendia dizer originalmente) que, com RAII e exceções, raramente uso ponteiros zero / NULL, mas às vezes você ainda precisa deles.

camh
fonte
9
espera, não é necessário um ponteiro nulo para avaliar como falso, independentemente de nulo ser zero internamente ou não?
Mooing Duck

Respostas:

185

Aqui está a opinião de Stroustrup sobre isso: Perguntas frequentes sobre estilo e técnica em C ++

Em C ++, a definição de NULLé 0, então há apenas uma diferença estética. Eu prefiro evitar macros, então uso 0. Outro problema NULLé que às vezes as pessoas acreditam erroneamente que é diferente de 0 e / ou não é um número inteiro. No código pré-padrão, NULLfoi / às vezes definido como algo inadequado e, portanto, teve / deve ser evitado. Isso é menos comum hoje em dia.

Se você precisar nomear o ponteiro nulo, chame-o nullptr; é assim que é chamado em C ++ 11. Então, nullptrserá uma palavra-chave.

Dito isto, não se preocupe com as pequenas coisas.

Martin Cote
fonte
7
Bjarne escreveu isso antes do C ++ 0x começar a trabalhar em um novo tipo nulo. Será o caso de NULL ser usado para esse tipo quando estiver disponível para uma plataforma, e acho que você verá uma alteração C no consenso geral sobre isso.
Richard Corden
122

Existem alguns argumentos (um dos quais é relativamente recente) que acredito contradizerem a posição de Bjarne sobre isso.

  1. Documentação de intenção

O uso NULLpermite pesquisas sobre seu uso e também destaca que o desenvolvedor queria usar um NULLponteiro, independentemente de ele estar sendo interpretado NULLou não pelo compilador .

  1. Sobrecarga de ponteiro e 'int' é relativamente rara

O exemplo que todo mundo cita é:

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

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

No entanto, pelo menos na minha opinião, o problema com o exposto acima não é que estamos usando NULL para a constante de ponteiro nulo, é que temos sobrecargas de 'foo' que usam tipos de argumentos muito diferentes. O parâmetro também deve ser um int, pois qualquer outro tipo resultará em uma chamada ambígua e, portanto, gerará um aviso útil do compilador.

  1. As ferramentas de análise podem ajudar HOJE!

Mesmo na ausência de C ++ 0x, existem ferramentas disponíveis hoje que verificam se NULLestá sendo usado para ponteiros e se 0está sendo usado para tipos integrais.

  1. O C ++ 11 terá um novo std::nullptr_ttipo.

Este é o argumento mais recente da tabela. O problema de 0e NULLestá sendo tratado ativamente no C ++ 0x, e você pode garantir que, para cada implementação que forneça NULL, a primeira coisa que eles farão é:

#define NULL  nullptr

Para aqueles que usam NULLem vez de 0, a mudança será uma melhoria no tipo de segurança com pouco ou nenhum esforço - se alguma coisa ele também pode pegar alguns bugs onde eles utilizados NULLpara 0. Para quem usa 0hoje ... bem ... espero que eles tenham um bom conhecimento de expressões regulares ...

Richard Corden
fonte
1
Esses são alguns pontos bastante bons, devo admitir. Fico feliz que C ++ 0x terá um tipo nulo, acho que isso tornará muitas coisas mais limpas.
7268 Rob
2
@ Richard, por que não fazer o oposto? Você pode usar o Meyers nullptr_t e, quando 0x estiver disponível, remova #includee mantenha o lado seguro o tempo todo.
fnieto - Fernando Nieto 15/11/2009
15
#define NULL nullptrparece perigoso. Para melhor ou pior, muitos códigos herdados usam NULL para outras coisas que não sejam 0. Por exemplo, os identificadores são frequentemente implementados como algum tipo integral, e configurá-los como NULLnão é incomum. Eu já vi abusos como usar NULLpara definir um charpara um terminador zero.
Adrian McCarthy
8
@AdrianMcCarthy: Eu diria apenas que isso é perigoso se houver o perigo de o código compilar silenciosamente e ter um significado diferente. Tenho certeza de que esse não é o caso; portanto, todos os usos incorretos do NULL seriam detectados.
Richard Corden
3
@RichardCorden: Hum, isso pressupõe que esses outros usos de NULLestão realmente incorretos. Muitas APIs são usadas há muito tempo NULLcom alças, e esse é realmente o uso documentado de muitas delas. Não é pragmático quebrá-los de repente e declarar que eles estão fazendo errado.
Adrian McCarthy
45

Use NULL. NULL mostra sua intenção. O fato de ser 0 é um detalhe de implementação que não deve importar.

Andy Lester
fonte
28
0 não é um detalhe de implementação. O padrão define 0 como o padrão de bits que representa um ponteiro nulo.
Ferruccio
5
Até parece ..!! Cara, C ++ é uma linguagem de baixo nível! Use 0, é um idioma bem conhecido.
hasen
8
Eu entendo que isso faz parte do padrão. É um detalhe de implementação no que diz respeito à leitura do código. O leitor deve pensar "ponteiro NULL" e não "0, que neste caso significa ponteiro NULL, não um número com o qual eu possa fazer aritmética".
Andy Lester
2
+1. Concordo com Andy. @Ferruccio, os detalhes de implementação da ideia do programador não são os mesmos que a implementação do compilador definida
user
se você usar NULL, em um código simples sem cabeçalho complexo, você encontrará o erro "NULL não está definido neste escopo".
ArtificiallyIntelligence
37

Eu sempre uso:

  • NULL para ponteiros
  • '\0' para caracteres
  • 0.0 para carros alegóricos e duplos

onde 0 faria bem. É uma questão de sinalizar intenção. Dito isto, eu não sou anal sobre isso.

Andrew Stein
fonte
24
ya provavelmente deve usar 0.0f para carros alegóricos, para evitar o typecast implícita
EvilTeach
35

Parei de usar NULL em favor de 0 há muito tempo (assim como a maioria das outras macros). Fiz isso não apenas porque queria evitar o máximo possível de macros, mas também porque NULL parece ter sido superutilizado no código C e C ++. Parece ser usado sempre que um valor 0 é necessário, não apenas para ponteiros.

Em novos projetos, coloquei isso no cabeçalho do projeto:

static const int nullptr = 0;

Agora, quando chegam os compiladores compatíveis com C ++ 0x, tudo o que preciso fazer é remover essa linha. Uma boa vantagem disso é que o Visual Studio já reconhece o nullptr como uma palavra-chave e o destaca adequadamente.

Ferruccio
fonte
4
Usar NULL será mais portátil a longo prazo. 'nullptr' estará disponível para algumas plataformas e não para outras. Sua solução aqui exige que você use o pré-processador em torno de sua declaração para garantir que ele esteja presente apenas quando necessário. NULL fará isso automaticamente.
Richard Corden
6
Discordo. Será menos portátil a curto prazo até que os compiladores os alcancem. A longo prazo, será igualmente portátil e talvez um pouco mais legível.
Ferruccio
4
Além disso, você sempre pode #define nullptr NULL para seu compilador não C ++ 0x.
Anteru
20
    cerr << sizeof(0) << endl;
    cerr << sizeof(NULL) << endl;
    cerr << sizeof(void*) << endl;

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

A moral da história. Você deve usar NULL quando estiver lidando com ponteiros.

1) Declara sua intenção (não me faça pesquisar em todo o seu código tentando descobrir se uma variável é um ponteiro ou algum tipo numérico).

2) Em determinadas chamadas de API que esperam argumentos variáveis, eles usarão um ponteiro NULL para indicar o final da lista de argumentos. Nesse caso, usar um '0' em vez de NULL pode causar problemas. Em uma plataforma de 64 bits, a chamada va_arg deseja um ponteiro de 64 bits, mas você passará apenas um número inteiro de 32 bits. Parece-me que você está confiando nos outros 32 bits para ser zerado para você? Eu vi alguns compiladores (por exemplo, icpc da Intel) que não são tão gentis - e isso resultou em erros de tempo de execução.

abonet
fonte
NULLtalvez não seja portátil e não seja seguro. Pode haver plataformas que ainda #define NULL 0(de acordo com a FAQ do Stroustrup: Devo usar NULL ou 0? Citado pela pergunta principal e ele está entre os primeiros resultados da pesquisa). Pelo menos no C ++ mais antigo, 0tem um significado conceitual especial no contexto do ponteiro. Você não deve pensar concretamente em bits. Observe também que em diferentes contextos inteiros ( short, int, long long) " sizeof(0)" vai ser diferente. Eu acho que essa resposta é um pouco equivocada.
Foof
(Pessoalmente, como programador C na vida cotidiana, vim visitar esta pergunta para entender por que as pessoas querem usar, em NULLvez de (char *)0, (const char *)0ou (struct Boo *)0ou (void *)0ou o que quer, expressar a intenção com mais clareza - sem ser (na minha opinião) muito complicado.)
FooF
Vote. está acontecendo no compilador msvc2013 C. em 64 bits, 0 quando converter para um ponteiro não garante ser NULL Pointer.
precisa saber é o seguinte
16

Se bem me lembro, NULL é definido de maneira diferente nos cabeçalhos que usei. Para C, é definido como (void *) 0, e para C ++, é definido como apenas 0. O código se parecia com:

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

Pessoalmente, ainda uso o valor NULL para representar ponteiros nulos, deixando explícito que você está usando um ponteiro em vez de algum tipo integral. Sim internamente, o valor NULL ainda é 0, mas não é representado como tal.

Além disso, não confio na conversão automática de números inteiros em valores booleanos, mas os comparo explicitamente.

Por exemplo, prefira usar:

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

ao invés de:

if (pointer_value || !integer_value)

Basta dizer que tudo isso é remediado no C ++ 11, onde se pode simplesmente usar em nullptrvez de NULL, e também nullptr_tesse é o tipo de a nullptr.

Daemin
fonte
15

Eu diria que a história falou e aqueles que argumentaram a favor do uso de 0 (zero) estavam errados (incluindo Bjarne Stroustrup). Os argumentos a favor de 0 eram principalmente estética e "preferência pessoal".

Após a criação do C ++ 11, com seu novo tipo nullptr, alguns compiladores começaram a reclamar (com parâmetros padrão) sobre a passagem de 0 para funções com argumentos de ponteiro, porque 0 não é um ponteiro.

Se o código tivesse sido escrito usando NULL, uma simples pesquisa e substituição poderia ter sido realizada através da base de código para torná-lo nullptr. Se você está preso ao código escrito usando a opção 0 como ponteiro, é muito mais entediante atualizá-lo.

E se você tiver que escrever um novo código agora no padrão C ++ 03 (e não puder usar o nullptr), deverá realmente usar o NULL. Isso facilitará muito a atualização no futuro.

Gaute Lindkvist
fonte
11

Eu normalmente uso 0. Não gosto de macros e não há garantia de que algum cabeçalho de terceiros que você está usando não redefina NULL para ser algo estranho.

Você pode usar um objeto nullptr como proposto por Scott Meyers e outros até que o C ++ obtenha uma palavra-chave 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" para mais informações.

jon-hanson
fonte
9
Qualquer biblioteca de terceiros que define NULL para algo diferente de 0 (ou (void*)0se estiver sendo compilado como código C) está apenas pedindo problemas e não deve ser usada.
23420 Adam Rosenfield
2
Você já viu uma biblioteca que redefine NULL? Sempre? Se uma biblioteca desse tipo já existisse, você teria problemas maiores que o NULL redefinido, como se estivesse usando uma biblioteca suficientemente burra para redefinir NULL.
precisa saber é o seguinte
1
Há mais de uma década, lembro-me vagamente de ter que lidar com alguns cabeçalhos de terceiros, possivelmente Orbix ou ObjectStore, que definiam NULL. Acho que tenho um ódio patológico por macros depois de perder vários dias e noites tentando fazer com que vários cabeçalhos de terceiros trabalhem com o windows.h.
precisa saber é o seguinte
2
"não gosto de macros" é uma crítica estranha a um #define semelhante a um objeto. Talvez você queira dizer que não gosta do pré-processador C?
Andrew Prock 12/02
@ Andrew - O único benefício de NULLmais (type *)0é pesquisabilidade, parece-me. Caso contrário, parece ofuscação desnecessária se não fosse um idioma C. Pessoalmente, acho que o idioma de se espalhar NULLpor todo o lugar merece morrer. NULLé macro inútil na minha opinião. Navalha de Occam tem algum trabalho a fazer aqui ...
Foof
11

Certa vez, trabalhei em uma máquina em que 0 era um endereço válido e NULL foi definido como um valor octal especial. Nessa máquina (0! = NULL), código como

char *p;

...

if (p) { ... }

não funcionaria como você espera. Você teve que escrever

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

Embora eu acredite que a maioria dos compiladores defina NULL como 0 atualmente, ainda me lembro da lição daqueles anos atrás: NULL não é necessariamente 0.

mxg
fonte
26
Você não estava usando um compilador compatível. O padrão diz que NULL é 0 e que o compilador deve converter 0 em um contexto de ponteiro em um valor NULL verdadeiro adequado para o arco.
Evan Teran
17
Sim você está certo. Isso aconteceu em meados dos anos 80, antes da ANSI produzir um padrão C. Não havia conformidade e os escritores de compiladores eram livres para interpretar o idioma como quisessem. É por isso que um padrão era necessário.
Mxg 8/10/08
9

Eu acho que o padrão garante que NULL == 0, então você pode fazer qualquer um. Prefiro NULL porque documenta sua intenção.

Mark Ransom
fonte
Se você tem estruturas aninhadas, acho que dizer foo.bar_ptr = (Bar *) 0expressa a intenção muito mais clara do que foo.bar_ptr = NULL. Esse hábito também capacita o compilador a detectar erros de concepção errada para você. Para mim, foo.bar_ptr = 0expressa a intenção e o uso, NULLse eu sei que foo.bar_ptré um ponteiro.
Foof
9

Usar 0 ou NULL terá o mesmo efeito.

No entanto, isso não significa que ambas sejam boas práticas de programação. Dado que não há diferença no desempenho, a escolha de uma opção com reconhecimento de baixo nível sobre uma alternativa agnóstica / abstrata é uma prática ruim de programação. Ajude os leitores do seu código a entender seu processo de pensamento .

NULL, 0, 0,0, '\ 0', 0x00 e tudo o mais são traduzidos para a mesma coisa, mas são entidades lógicas diferentes no seu programa. Eles devem ser usados ​​como tal. NULL é um ponteiro, 0 é quantidade, 0x0 é um valor cujos bits são interessantes etc. Você não atribuiria '\ 0' a um ponteiro, seja ele compilado ou não.

Sei que algumas comunidades incentivam a demonstrar conhecimento profundo de um ambiente, quebrando os contratos do ambiente. Programadores responsáveis, no entanto, criam código sustentável e mantêm essas práticas fora de seu código.

Chris
fonte
5

Estranho, ninguém, incluindo Stroustroup mencionou isso. Ao falar muito sobre padrões e estética, ninguém percebeu que é perigoso usá-lo 0em NULLseu lugar, por exemplo, na lista de argumentos variáveis ​​na arquitetura em que sizeof(int) != sizeof(void*). Como Stroustroup, prefiro 0por razões estéticas, mas é preciso ter cuidado para não usá-lo onde seu tipo possa ser ambíguo.

Michael Krelin - hacker
fonte
E naqueles lugares perigosos você ainda pode usar 0desde que você especificar que 0você quer dizer - por exemplo (int *)0, (char *)0, (const char *)0ou (void *)0ou (unsigned long long) 0ou qualquer outra coisa. Na minha opinião, isso expressa a intenção muito mais clara do que NULL.
Foof
1
Claro, se você não sabe o que NULLsignifica.
Michael Krelin - hacker de
Pessoalmente, acho um pouco desagradável lançar algo desnecessariamente para (void *)quando eu pudesse usar o tipo exato. De propósito, dei um exemplo (geralmente) de número inteiro de 64 bits na lista porque é análogo ao caso do ponteiro. Além disso, se minha lembrança de que C ++ mais antigo definiu NULLcomo 0precisa (faz anos desde que eu programei em C ++), não testemunhamos nenhuma melhoria na correção do programa. Felizmente, o padrão C ++ mais recente fornece nullptrpalavras-chave, para que possamos nos livrar dessa NULLfeiúra e de toda a controvérsia ao escrever C ++ mais recente.
Foof
Bem, é por isso que o elenco de (void*)foi abstraído NULL. E, NULLna verdade, expressa claramente a intenção na maioria das vezes. E acho que sua lembrança está errada. Não tenho certeza sobre os padrões, mas, na prática, acredito que tenha sido (void*)0. E sim, nullptré um bom prettificador, embora seja o mesmo NULL- especificar ponteiro nulo sem especificar o tipo.
Michael Krelin - hacker de
1
@FooF, em algumas plataformas - talvez. Na minha realidade, funcionou e, portanto, suspeito que tenha sido definido como um ponteiro. Quanto à robustez, sim, o que eu estava tentando dizer que usar nullptrtraz a mesma mensagem que NULL, era apenas para expressar a intenção que você mencionou no começo. (Pré NULL- processamento de gccrendimentos modernos __null, seja o que for).
Michael Krelin - hacker de
4

Eu tento evitar toda a pergunta usando referências C ++ sempre que possível. Ao invés de

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

você pode frequentemente escrever

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

Claro, isso nem sempre funciona; mas ponteiros nulos podem ser usados ​​em excesso.

Como
fonte
3

Estou com o Stroustrup neste :-) Como o NULL não faz parte do idioma, prefiro usar 0.

Roubar
fonte
3

Preferência principalmente pessoal, embora se possa argumentar que NULL torna bastante óbvio que o objeto é um ponteiro que atualmente não aponta para nada, por exemplo

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

IIRC, o padrão não exige que NULL seja 0, portanto, usar o que estiver definido em <stddef.h> provavelmente é o melhor para o seu compilador.

Outra faceta do argumento é se você deve usar comparações lógicas (conversão implícita para bool) ou verificação de explicitação contra NULL, mas isso também se resume à legibilidade.

Jimmy
fonte
3

Prefiro usar NULL, pois deixa claro que sua intenção é que o valor represente um ponteiro e não um valor aritmético. O fato de ser uma macro é lamentável, mas, uma vez que é tão amplamente arraigado, há pouco perigo (a menos que alguém faça algo realmente tímido). Eu gostaria que fosse uma palavra-chave desde o início, mas o que você pode fazer?

Dito isto, não tenho nenhum problema em usar ponteiros como valores de verdade em si mesmos. Assim como no NULL, é um idioma arraigado.

O C ++ 09 adicionará a construção nullptr que acho que está muito atrasada.

Michael Burr
fonte
1

Eu sempre uso 0. Não por qualquer motivo real, apenas porque quando eu estava aprendendo C ++, eu li algo que recomendava o uso de 0 e sempre fiz dessa maneira. Em teoria, poderia haver um problema de confusão na legibilidade, mas, na prática, nunca encontrei esse problema em milhares de horas-homem e milhões de linhas de código. Como Stroustrup diz, é realmente apenas uma questão estética pessoal até que o padrão se torne nulo.

Gerald
fonte
1

Alguém me disse uma vez ... Vou redefinir NULL para 69. Desde então, não o uso: P

Isso torna seu código bastante vulnerável.

Editar:

Nem tudo no padrão é perfeito. A macro NULL é uma constante de ponteiro nulo C ++ definida pela implementação que não é totalmente compatível com a macro C NULL, que além do tipo oculto implícito a converte em uma ferramenta inútil e propensa a erros.

NULL não se comporta como um ponteiro nulo, mas como um literal de O / OL.

Diga-me que o próximo exemplo não é confuso:

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

É por tudo isso, no novo padrão aparece std :: nullptr_t

Se você não quiser esperar pelo novo padrão e quiser usar um nullptr, use pelo menos um decente como o proposto por Meyers (consulte o comentário jon.h).

fnieto - Fernando Nieto
fonte
5
NULLé uma parte bem definida do padrão C ++. Permitir que as pessoas que gostam de redefinir macros padrão editem o código em seu projeto torna seu código 'vulnerável'; usando NULLnão.
22411 CB Bailey
1

Bem, eu argumento por não usar ponteiros 0 ou NULL, sempre que possível.

Usá-los mais cedo ou mais tarde levará a falhas de segmentação no seu código. Na minha experiência, isso e ponteiros em gereral é uma das maiores fontes de bugs em C ++

Além disso, leva a instruções "if-not-null" em todo o seu código. Muito melhor se você puder confiar sempre em um estado válido.

Quase sempre há uma alternativa melhor.

Jan P
fonte
2
Uma falha de segmentação garantida (e é garantida em sistemas modernos quando você desreferencia 0) é útil para depuração. Muito melhor do que desreferenciar o lixo aleatório e obter quem sabe qual resultado.
Lightness Races in Orbit
-4

Definir um ponteiro para 0 simplesmente não é tão claro. Especialmente se você vir uma linguagem diferente de C ++. Isso inclui C e Javascript.

Recentemente delt com algum código como este:

virtual void DrawTo(BITMAP *buffer) =0;

pela função virtual pura pela primeira vez. Eu pensei que fosse algum jiberjash mágico por uma semana. Quando percebi que estava basicamente configurando o ponteiro de função para a null(como funções virtuais são apenas ponteiros de função na maioria dos casos para C ++), me chutei.

virtual void DrawTo(BITMAP *buffer) =null;

teria sido menos confuso do que aquela conversa sem espaçamento adequado para os meus novos olhos. Na verdade, estou me perguntando por que o C ++ não emprega letras minúsculas, nullassim como emprega letras minúsculas falsas e verdadeiras agora.

Pizzach
fonte
Em geral, prefiro NULl a 0 para ponteiros. No entanto '= 0;' é a maneira idiomática de declarar uma função virtual pura em C ++. Eu recomendo fortemente que você não use '= NULL;' para este caso em particular.
Danio
Este é o comentário mais engraçado no StackOverflow. Você provavelmente já sabe agora que o exemplo que você deu é uma sintaxe para função virtual pura e não um ponteiro. E sim, @danio está certo, você não deve usar NULL para pura função virtual.
sgowd