Posso usar NULL como substituição do valor 0?


73

Posso usar o NULLponteiro como substituto do valor de 0?

Ou há algo de errado nisso?


Como por exemplo:

int i = NULL;

como substituto para:

int i = 0;

Como experimento, compilei o seguinte código:

#include <stdio.h>

int main(void)
{
    int i = NULL;
    printf("%d",i);

    return 0;
}

Resultado:

0

De fato, ele me dá esse aviso, que é completamente correto por si só:

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 

mas o resultado ainda é equivalente.


  • Estou entrando no "comportamento indefinido" com isso?
  • É permitido utilizar NULLdessa maneira?
  • Há algo de errado em usar NULLcomo valor numérico em expressões aritméticas?
  • E qual é o resultado e o comportamento em C ++ para este caso?

Eu li as respostas de Qual é a diferença entre NULL, '\ 0' e 0 sobre qual é a diferença entre NULL, \0e 0é, mas não recebi as informações concisas a partir daí, se é bastante permitido e correto usar NULLcomo valor para operar em atribuições e outras operações aritméticas.


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Samuel Liew

Seria realmente melhor fazer duas perguntas separadas, uma para C e outra para C ++.
Konrad Rudolph

Respostas:


82

Posso usar o ponteiro NULL como substituto do valor 0?

Não , não é seguro fazê-lo. NULLé uma constante de ponteiro nulo, que pode ter tipo int, mas que normalmente tem tipo void *(em C) ou não é diretamente atribuível a um int(em C ++> = 11). Os dois idiomas permitem que os ponteiros sejam convertidos em números inteiros, mas eles não permitem que essas conversões sejam executadas implicitamente (embora alguns compiladores o ofereçam como uma extensão). Além disso, embora seja comum converter um ponteiro nulo em um número inteiro para gerar o valor 0, o padrão não garante isso. Se você deseja uma constante com tipo inte valor 0, soletre-a 0.

  • Posso passar para o comportamento indefinido com isso?

Sim, em qualquer implementação em que se NULLexpanda para um valor com o tipo void *ou qualquer outro não diretamente atribuível int. O padrão não define o comportamento de sua atribuição em tal implementação; portanto, seu comportamento é indefinido.

  • é permitido operar com o NULL dessa maneira?

É um estilo ruim, e irá quebrar em alguns sistemas e sob algumas circunstâncias. Na medida em que você parece estar usando o GCC, ele seria quebrado em seu próprio exemplo se você compilasse com a -Werroropção

  • Existe algo errado em usar NULL como valor numérico em expressões aritméticas?

Sim. Não é garantido que você tenha um valor numérico. Se você quer dizer 0, escreva 0, que não é apenas bem definido, mas mais curto e mais claro.

  • E como é o resultado em C ++ nesse caso?

A linguagem C ++ é mais rígida em conversões do que em C e possui regras diferentes para NULL, mas também as implementações podem fornecer extensões. Novamente, se você quer dizer 0, é isso que deve escrever.


4
Você deve apontar que "o que normalmente tem tipo void *" é válido apenas para C. void *não é um tipo legal para C ++ (porque você não pode atribuir void*a nenhum outro tipo de ponteiro). No C ++ 89 e C ++ 03, de fato, NULL deve ser do tipo int, mas em versões posteriores pode ser (e geralmente é) nullptr_t.
Martin Bonner apoia Monica

Você também está errado ao converter void*para intum comportamento indefinido. Não é; é um comportamento especificado de implementação.
Martin Bonner apoia Monica

@MartinBonnersupportsMonica, Nos contextos em que C especifica que um ponteiro é convertido em um número inteiro, o resultado da conversão é realmente especificado pela implementação, mas não é disso que estou falando. É a atribuição de um ponteiro a um lvalue do tipo inteiro (sem conversão explícita por meio de uma conversão) que possui um comportamento indefinido. O idioma não define uma conversão automática lá.
John Bollinger

@MartinBonnersupportsMonica, editei para incluir mais as considerações de C ++. De qualquer forma, o tema central se aplica igualmente aos dois idiomas: se você deseja um número inteiro 0, escreva-o explicitamente como uma constante inteira do tipo apropriado.
John Bollinger

31

NULLé alguma constante de ponteiro nulo. Em C, poderia ser uma expressão constante inteira com valor 0ou uma expressão lançada em void*, com a última mais provável. O que significa que você não pode assumir o uso de forma NULLintercambiável com zero. Por exemplo, neste exemplo de código

char const* foo = "bar"; 
foo + 0;

A substituição 0por NULLnão é garantida como um programa C válido, porque a adição entre dois ponteiros (sem falar em tipos diferentes de ponteiros) não está definida. Isso fará com que um diagnóstico seja emitido devido a uma violação de restrição. Os operandos para adição não serão válidos .


Quanto ao C ++, as coisas são um pouco diferentes. A falta de uma conversão implícita de void*para outros tipos de objetos significava que ela NULLera historicamente definida como 0no código C ++. No C ++ 03, você provavelmente poderia se safar. Mas desde o C ++ 11 ele pode sernullptr legalmente definido como a palavra - chave . Agora, novamente, produzindo um erro, pois std::nullptr_tnão pode ser adicionado aos tipos de ponteiro.

Se NULLfor definido como tal nullptr, mesmo sua experiência se tornará inválida. Não há conversão de std::nullptr_tpara um número inteiro. É por isso que é considerada uma constante de ponteiro nulo mais segura.


Para completar, 0L também é uma constante de ponteiro nulo e pode ser usado como NULLnos dois idiomas.
eerorika

11
@jamesqf O padrão diz que a constante inteira com o valor 0 é uma constante de ponteiro nulo. Portanto, 0L é uma constante de ponteiro nulo.
eerorika

11
@eerorika: Exatamente o que o mundo precisa, padrões que ignoram a realidade :-) Porque, se estou lembrando corretamente da montagem 80286, você não pode nem atribuir um ponteiro distante como uma única operação, para que os escritores do compilador precisem -casa.
Jamesqf 23/12/19

2
@jamesqf De acordo com a FAQ C , re: tornando 0constante um ponteiro nulo: "evidentemente como um sopro para todo o código C existente, mal escrito, que fez suposições incorretas" #
911 Andrew Henle

3
@ jamesqf, que toda e qualquer constante inteira com o valor 0 é uma constante de ponteiro nulo (em C) não tem nada a ver com implementações de ponteiro de hardware. Observe também que o padrão C não reconhece uma distinção entre ponteiros próximos e distantes em qualquer caso, mas suporta atribuições de ponteiro para ponteiro. Ele também suporta comparações de ponteiros, que apresentam questões interessantes para formatos de endereçamento segmentado, como o 286.
John Bollinger

21

Posso usar o ponteiro NULL como substituto do valor 0?

int i = NULL;

As regras variam entre os idiomas e suas versões. Em alguns casos, você pode e em outros, você não pode. Independentemente, você não deveria . Se você tiver sorte, seu compilador avisará quando você tentar ou, melhor ainda, não conseguir compilar.

Em C ++, anterior ao C ++ 11 (citação de C ++ 03):

[lib.support.types]

NULL é um ponteiro nulo C ++ definido por implementação constante neste Padrão Internacional.

Não faz sentido usar uma constante de ponteiro nulo como um número inteiro. Contudo...

[conv.ptr]

Uma constante de ponteiro nulo é um valor de expressão constante integral (5.19) do tipo inteiro que é avaliado como zero.

Portanto, tecnicamente funcionaria mesmo que seja sem sentido. Devido a esse detalhe técnico, você pode encontrar programas mal escritos que abusam NULL.

Desde C ++ 11 (citação do último rascunho):

[conv.ptr]

Uma constante de ponteiro nulo é um número inteiro literal ([lex.icon]) com o valor de zero ou um prvalue de tipo std :: nullptr_t .

A std​::​nullptr_­tnão é conversível em um número inteiro, portanto, usar NULLcomo número inteiro funcionaria apenas condicionalmente, dependendo das escolhas feitas pela implementação do idioma.

PS nullptré um pré-valor do tipo std​::​nullptr_­t. A menos que você precise compilar seu programa no pré-C ++ 11, você deve sempre usar em nullptrvez de NULL.


C é um pouco diferente (citações do rascunho C11 N1548):

6.3.2.3 Idioma / conversões / outros operandos / ponteiros

3 Uma expressão constante inteira com o valor 0, ou essa expressão convertida em tipovoid * , é chamada constante de ponteiro nulo. ...

Portanto, o caso é semelhante ao pós C ++ 11, ou seja, o abuso de NULLobras condicionalmente, dependendo das escolhas feitas pela implementação da linguagem.


10

Sim , embora, dependendo da implementação, você possa precisar de uma conversão. Mas sim, é 100% legítimo, caso contrário.

Embora seja um estilo muito, muito, muito ruim (escusado será dizer?).

NULLé, ou na verdade não é C ++, é C. O padrão , no entanto, como em muitos legados de C, possui duas cláusulas ([diff.null] e [support.types.nullptr]) que tecnicamente compõem o NULLC ++. É uma constante de ponteiro nulo definida pela implementação . Portanto, mesmo que seja de estilo ruim, é tecnicamente o C ++ possível.
Conforme apontado na nota de rodapé , possíveis implementações podem ser 0ou 0L, mas não (void*)0 .

NULLpoderia, é claro (o padrão não diz isso explicitamente, mas é praticamente a única opção que resta depois 0ou 0L) nullptr. Quase nunca é o caso, mas é uma possibilidade legal.

O aviso que o compilador mostrou a você demonstra que o compilador não é realmente compatível (a menos que você tenha compilado no modo C). Porque, bem, de acordo com o aviso, ele converteu um ponteiro nulo (não o nullptrque seria nullptr_t, o que seria distinto), então, aparentemente, a definição de NULLé de fato (void*)0, o que pode não ser.

De qualquer forma, você tem dois possíveis casos legítimos (ou seja, o compilador não está quebrado). Ou (o caso realista), NULLé algo como 0ou 0L, então você tem "zero ou uma" conversão em número inteiro e está pronto para prosseguir.

Ou NULLé de fato nullptr. Nesse caso, você tem um valor distinto que garante garantias de comparação, bem como conversões claramente definidas de números inteiros, mas infelizmente não para números inteiros. No entanto, possui uma conversão claramente definida para bool(resultando em false) e booluma conversão claramente definida para número inteiro (resultando em 0).

Infelizmente, são duas conversões, portanto, não está dentro de "zero ou um", como apontado em [conv]. Portanto, se sua implementação definir NULLcomo nullptr, você precisará adicionar uma conversão explícita para que seu código esteja correto.


6

Do FAQ C:

P: Se NULL e 0 são equivalentes como constantes de ponteiro nulo, qual devo usar?

A: É apenas em contextos de ponteiro que NULLe 0são equivalentes. nãoNULL deve ser usado quando outro tipo de 0 for necessário, mesmo que funcione, pois isso envia a mensagem estilística errada. (Além disso, o ANSI permite que a definição de NULL seja , o que não funcionará em contextos sem ponteiros.) Em particular, não use quando o caractere nulo ASCII (NUL) for desejado. Forneça sua própria definição((void *)0)NULL

http://c-faq.com/null/nullor0.html


5

Disclaimer: Eu não sei C ++. Minha resposta não deve ser aplicada no contexto de C ++

'\0'é um intcom valor zero, exatamente 100% exatamente como 0.

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

No contexto de ponteiros , 0e NULLsão 100% equivalentes:

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

são todos 100% equivalentes.


Nota sobre ptr + NULL

O contexto de nãoptr + NULL é o de ponteiros. Não há definição para a adição de ponteiros na linguagem C; ponteiros e números inteiros podem ser adicionados (ou subtraídos). Em se quer ou é um ponteiro, o outro deve ser um inteiro, então é efetivamente ou e, dependendo das definições de e vários comportamentos pode ser esperado: tudo trabalho, alertando para a conversão entre ponteiro e inteiro, a falta de compilar, .. .ptr + NULLptrNULLptr + NULL(int)ptr + NULLptr + (int)NULLptrNULL


Eu já vi #define NULL (void *)0antes. Tem certeza de que NULL e simples 0 são 100% equivalentes?
machine_1

2
No contexto do ponteiro, sim ... condição enfatizada na minha resposta, obrigado
pmg 21/12/19

@ phuclv: Eu não tenho absolutamente nenhuma idéia sobre C ++. Minha resposta (exceto o bit entre parênteses) é sobre C
pmg

@phuclv ptr + NULLnão está usando NULLno contexto de ponteiros #
pmg

3
@JesperJuhl: no contexto de ponteiros, eles são 100% equivalentes. Eu não tenho nenhuma idéia do que nullptré, mas ((void*)0)e 0(ou '\0') são equivalentes no contexto de ponteiros ...if (ptr == '\0' /* or equivalent 0, NULL */)
pmg

5

Não, não é mais o preferido NULL(maneira antiga de inicialização do ponteiro).

Desde o C ++ 11:

A palavra-chave nullptrdenota o literal do ponteiro. É um pré-valor do tipo std :: nullptr_t. Existem conversões implícitas de nullptrpara o valor do ponteiro nulo de qualquer tipo de ponteiro e qualquer ponteiro para o tipo de membro. Conversões semelhantes existem para qualquer constante de ponteiro nulo, que inclui valores do tipo std::nullptr_te da macro NULL.

https://en.cppreference.com/w/cpp/language/nullptr

Na verdade, std :: nullptr_t é o tipo do literal do ponteiro nulo nullptr,. É um tipo distinto que não é, por si só, um tipo de ponteiro ou um ponteiro para o tipo de membro.

#include <cstddef>
#include <iostream>

void f(int* pi)
{
   std::cout << "Pointer to integer overload\n";
}

void f(double* pd)
{
   std::cout << "Pointer to double overload\n";
}

void f(std::nullptr_t nullp)
{
   std::cout << "null pointer overload\n";
}

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(0);  // ambiguous call: all three functions are candidates
    // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                // (as is the case in most implementations)
}

Resultado:

Pointer to integer overload
Pointer to double overload
null pointer overload

A questão é sobre a atribuição de NULL a 0 para números inteiros. Nesse sentido, nada muda com nullptr em vez de NULL.
ivan.ukr

Ele usou palavras como "ponteiro nulo".
Mannoj 25/12/19

A propósito, C ++ não possui o conceito NULL após o C ++ 11. O autor pode estar confuso sobre o uso do constexpr ou definir a inicialização da maneira antiga. pt.cppreference.com/w/cpp/language/default_initialization
Mannoj
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.