Qual é a diferença entre NULL, '\ 0' e 0?

309

Em C, parece haver diferenças entre os vários valores de zero - NULL, NULe 0.

Eu sei que o caractere ASCII é '0'avaliado como 48ou 0x30.

O NULLponteiro é geralmente definido como:

#define NULL 0

Ou

#define NULL (void *)0

Além disso, há o NULpersonagem '\0'que parece avaliar 0também.

Há momentos em que esses três valores não podem ser iguais?

Isso também é verdade em sistemas de 64 bits?

gnavi
fonte
1
Consulte stackoverflow.com/questions/176989/… para obter mais informações sobre as diferenças entre 0 e NULL.
David Rodríguez - dribeas
7
O identificador NULnão existe na linguagem ou biblioteca padrão C (ou em C ++, tanto quanto eu sei). O caractere nulo às vezes é chamado de NUL, mas em C ou C ++ geralmente é chamado apenas de '\0'.
Keith Thompson

Respostas:

351

Nota: Esta resposta se aplica à linguagem C, não a C ++.


Ponteiros nulos

A constante inteira literal 0tem significados diferentes, dependendo do contexto em que é usada. Em todos os casos, ainda é uma constante inteira com o valor 0, é apenas descrito de maneiras diferentes.

Se um ponteiro estiver sendo comparado ao literal constante 0, essa é uma verificação para ver se o ponteiro é um ponteiro nulo. Isso 0é referido como uma constante de ponteiro nulo. O padrão C define que a 0conversão para o tipo void *é um ponteiro nulo e uma constante de ponteiro nulo.

Além disso, para ajudar na legibilidade, a macro NULLé fornecida no arquivo de cabeçalho stddef.h. Dependendo do seu compilador, pode ser possível #undef NULLredefini-lo para algo maluco.

Portanto, aqui estão algumas maneiras válidas de verificar um ponteiro nulo:

if (pointer == NULL)

NULLé definido para comparar igual a um ponteiro nulo. É definido como implementação qual é a definição real NULL, desde que seja uma constante válida de ponteiro nulo.

if (pointer == 0)

0 é outra representação da constante de ponteiro nulo.

if (!pointer)

Essa ifdeclaração verifica implicitamente "não é 0", portanto, revertemos que significa "é 0".

A seguir, são apresentadas formas INVÁLIDAS de procurar um ponteiro nulo:

int mynull = 0;
<some code>
if (pointer == mynull)

Para o compilador, essa não é uma verificação para um ponteiro nulo, mas uma verificação de igualdade em duas variáveis. Isso pode funcionar se mynull nunca mudar no código e as otimizações do compilador dobrarem constantemente o 0 na instrução if, mas isso não é garantido e o compilador precisa produzir pelo menos uma mensagem de diagnóstico (aviso ou erro) de acordo com o Padrão C.

Observe que o que é um ponteiro nulo na linguagem C. Não importa na arquitetura subjacente. Se a arquitetura subjacente tiver um valor de ponteiro nulo definido como o endereço 0xDEADBEEF, será responsabilidade do compilador resolver essa bagunça.

Assim, mesmo nessa arquitetura engraçada, as seguintes maneiras ainda são válidas para verificar se há um ponteiro nulo:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)

A seguir, são apresentadas formas INVÁLIDAS de procurar um ponteiro nulo:

#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)

como estes são vistos por um compilador como comparações normais.

Caracteres nulos

'\0'é definido como um caractere nulo - ou seja, com todos os bits definidos como zero. Isso não tem nada a ver com ponteiros. No entanto, você pode ver algo semelhante a este código:

if (!*string_pointer)

verifica se o ponteiro da string está apontando para um caractere nulo

if (*string_pointer)

verifica se o ponteiro da string está apontando para um caractere não nulo

Não confunda isso com ponteiros nulos. Só porque a representação de bits é a mesma, e isso permite alguns casos convenientes de cruzamento, eles não são realmente a mesma coisa.

Além disso, '\0'é (como todos os literais de caracteres) uma constante inteira, nesse caso com o valor zero. Portanto, '\0'é completamente equivalente a uma 0constante inteira sem adornos - a única diferença está na intenção que ela transmite a um leitor humano ("estou usando isso como um caractere nulo").

Referências

Consulte a pergunta 5.3 das perguntas frequentes do comp.lang.c para obter mais informações. Veja este pdf para o padrão C. Confira as seções 6.3.2.3 Ponteiros, parágrafo 3.

Andrew Keeton
fonte
3
Obrigado por apontar para a lista de perguntas frequentes. No entanto, consulte também c-faq.com/null/nullor0.html
Sinan Ünür 18/08/09
4
Não, você não comparará ptrcom todos os bits zero . Não é um memcmp, mas é uma comparação usando um operador interno. Um lado é uma constante de ponteiro nulo '\0'e o outro lado é um ponteiro. Além das outras duas versões com NULLe 0. Esses três fazem as mesmas coisas.
Johannes Schaub - litb
6
Você está usando o operador de comparação interno como algo que compara cadeias de bits. Mas não é isso que é. Ele compara dois valores, que são conceitos abstratos. Assim, um ponteiro nulo que internamente é representado como 0xDEADBEEFainda é um ponteiro nulo, não importa o que sua aparência bitstring gosta, e ele ainda irá comparar igual a NULL, 0, \0e todas as outras formas constantes ponteiro nulo.
Johannes Schaub - litb
2
Você faz uma boa observação sobre o operador de comparação. Eu reparei no C99. Ele diz "Uma expressão constante inteira com o valor 0, ou uma expressão convertida para digitar void *, é chamada constante de ponteiro nulo". Ele também diz que um literal de caractere é uma expressão constante inteira. Assim, pela propriedade transitiva, você está certo ptr == '\0'.
Andrew Keeton
2
"... pode ser possível #undef NULL e redefini-lo para algo maluco. Qualquer um que fizer isso merece ser baleado." este meu bom senhor me fez rir em voz alta ...
oggiemc
34

Parece que várias pessoas não entendem quais são as diferenças entre NULL, '\ 0' e 0. Então, para explicar, e na tentativa de evitar repetir as coisas ditas anteriormente:

Uma expressão constante do tipo intcom o valor 0 ou uma expressão desse tipo, convertida em tipo, void *é uma constante de ponteiro nulo que, se convertida em um ponteiro, se torna um ponteiro nulo . É garantido pelo padrão comparar desigual com qualquer ponteiro com qualquer objeto ou função .

NULLé uma macro, definida como uma constante de ponteiro nulo .

\0é uma construção usada para representar o caractere nulo , usado para finalizar uma sequência.

Um caractere nulo é um byte que possui todos os seus bits definidos como 0.

amaterasu
fonte
14

Todos os três definem o significado de zero em diferentes contextos.

  • contexto do ponteiro - NULL é usado e significa que o valor do ponteiro é 0, independentemente de ser 32 bits ou 64 bits (um caso 4 bytes e outros 8 bytes de zeros).
  • contexto de cadeia - o caractere que representa o dígito zero possui um valor hexadecimal de 0x30, enquanto o caractere NUL possui um valor hexadecimal de 0x00 (usado para finalizar cadeias).

Estes três são sempre diferentes quando você olha para a memória:

NULL - 0x00000000 or 0x00000000'00000000 (32 vs 64 bit)
NUL - 0x00 or 0x0000 (ascii vs 2byte unicode)
'0' - 0x20

Espero que isso esclareça.

Nasko
fonte
8
Nasko: Avalie sizeof('\0')e surpreenda-se.
caf
3
@Nasko: Fiquei realmente surpreso: com gcc, em C: sizeof ('\ 0') == sizeof ('a') == 4, enquanto que com g ++, em C ++: sizeof ('\ 0') == sizeof ('a') == 1
David Rodríguez - dribeas
1
@Nasko: No padrão C (draft, n1124): 'Uma constante de caractere inteiro tem o tipo int', portanto '\ 0' é realmente do tipo int em C e, portanto, sizeof ('\ 0') é 4 na minha arquitetura (linux, 32bit)
David Rodríguez - dribeas
@dribeas - Eu não estava descrevendo isso como uma constante, e sim o que você veria como parte da string. Eu definitivamente poderia ter explicitado. Obrigado
Nasko
@ DavidRodríguez-dribeas Undid edit "Valor ASCII '0' corrigido para 0x20 (32 de dezembro)"
chux - Restabelece Monica
6

Se NULL e 0 são equivalentes como constantes de ponteiro nulo, qual devo usar? na lista de perguntas frequentes C também resolve esse problema:

Os programadores de C devem entender isso NULLe 0são intercambiáveis ​​em contextos de ponteiros, e que um uncast 0 é perfeitamente aceitável. Qualquer uso de NULL (em oposição a 0) deve ser considerado um lembrete gentil de que um ponteiro está envolvido; os programadores não devem depender disso (para seu próprio entendimento ou do compilador) para distinguir o ponteiro 0do número inteiro 0.

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

#define NUL '\0'

se você precisar.

Sinan Ünür
fonte
5

Qual é a diferença entre NULL, '\ 0' e 0

"caractere nulo (NUL)" é mais fácil de excluir. '\0'é um caractere literal. Em C, é implementado como int, portanto, é o mesmo que 0, que é de INT_TYPE_SIZE. No C ++, o literal de caractere é implementado como char1 byte. Normalmente é diferente de NULLou 0.

A seguir, NULLé um valor de ponteiro que especifica que uma variável não aponta para nenhum espaço de endereço. Deixando de lado o fato de que geralmente é implementado como zeros, ele deve poder expressar o espaço de endereço completo da arquitetura. Assim, em uma arquitetura de 32 bits, NULL (provável) tem 4 bytes e na arquitetura de 64 bits, 8 bytes. Isso depende da implementação de C.

Finalmente, o literal 0é do tipo int, que é do tamanho INT_TYPE_SIZE. O valor padrão de INT_TYPE_SIZEpode ser diferente dependendo da arquitetura.

A Apple escreveu:

O modelo de dados de 64 bits usado pelo Mac OS X é conhecido como "LP64". Este é o modelo de dados comum usado por outros sistemas UNIX de 64 bits da Sun e SGI, bem como pelo Linux de 64 bits. O modelo de dados LP64 define os tipos primitivos da seguinte maneira:

  • entradas são de 32 bits
  • os longos são de 64 bits
  • long-longs também são de 64 bits
  • ponteiros são de 64 bits

Wikipedia de 64 bits :

O compilador VC ++ da Microsoft usa o modelo LLP64.

64-bit data models
Data model short int long  long long pointers Sample operating systems
LLP64      16    32  32    64        64       Microsoft Win64 (X64/IA64)
LP64       16    32  64    64        64       Most Unix and Unix-like systems (Solaris, Linux, etc.)
ILP64      16    64  64    64        64       HAL
SILP64     64    64  64    64        64       ?

Editar : Adicionado mais no literal do caractere.

#include <stdio.h>

int main(void) {
    printf("%d", sizeof('\0'));
    return 0;
}

O código acima retorna 4 no gcc e 1 no g ++.

Eugene Yokota
fonte
2
Não, não'\0' é um valor de 1 byte. É um literal de caractere, que é uma expressão constante inteira - portanto, se pode-se dizer que ele tem um tamanho, é o tamanho de um (que deve ter pelo menos 2 bytes). Se você não acredita em mim, avalie e veja por si mesmo. , e são todos completamente equivalentes. intsizeof('\0')'\0'00x0
caf
@caf depende do idioma. Se você não acredita em mim, tente sizeof('\0')um compilador C ++.
Eugene Yokota
2
você deve usar "% zu" ao imprimir sizeof (algo)
Não utilizado
4

Um one-L NUL, termina uma string.

Um N-L de dois pontos não indica nada.

E eu vou apostar um touro de ouro

Que não há três L NULLL.

Como você lida com o NUL?

EvilTeach
fonte
4

Uma boa peça que me ajuda ao começar com C (extraído da Expert C Programming by Linden)

O Um 'l' nul e o Dois 'l' nulo

Memorize esta pequena rima para recuperar a terminologia correta para ponteiros e zero ASCII:

The one "l" NUL ends an ASCII string,

The two "l" NULL points to no thing.

Apologies to Ogden Nash, but the three "l" nulll means check your spelling. 

O caractere ASCII com o padrão de bit zero é denominado "NUL". O valor do ponteiro especial que significa que o ponteiro não aponta para lugar nenhum é "NULL". Os dois termos não têm significado intercambiável.

dlmeetei
fonte
Muito mais simples: NULé um código de controle, tais como BEL, VT, HT, SOTetc. e, portanto, tem max. 3 caracteres.
glglgl
2

"NUL" não é 0, mas refere-se ao caractere ASCII NUL. Pelo menos, é assim que eu vi isso ser usado. O ponteiro nulo geralmente é definido como 0, mas isso depende do ambiente em que você está executando e da especificação de qualquer sistema operacional ou idioma que esteja usando.

No ANSI C, o ponteiro nulo é especificado como o valor inteiro 0. Portanto, qualquer mundo em que isso não seja verdade não é compatível com ANSI C.

peterb
fonte
1

Um byte com um valor de 0x00é, na tabela ASCII, o caractere especial chamado NULor NULL. Em C, como você não deve incorporar caracteres de controle no seu código-fonte, isso é representado nas seqüências de caracteres C com um 0 escapado, ou seja \0,.

Mas um NULL verdadeiro não é um valor. É a ausência de um valor. Para um ponteiro, significa que o ponteiro não tem nada para apontar. Em um banco de dados, significa que não há valor em um campo (o que não é o mesmo que dizer que o campo está em branco, 0 ou preenchido com espaços).

O valor real que um determinado formato de arquivo de sistema ou banco de dados usa para representar a NULLnão é necessariamente 0x00.

richardtallent
fonte
0

NULLnão é garantido que seja 0 - seu valor exato depende da arquitetura. A maioria das principais arquiteturas o define (void*)0.

'\0' sempre será igual a 0, porque é assim que o byte 0 é codificado em um caractere literal.

Não me lembro se é necessário que os compiladores C usem ASCII - se não, '0'pode nem sempre ser igual a 48. Independentemente, é improvável que você encontre um sistema que use um conjunto de caracteres alternativo como EBCDIC, a menos que esteja trabalhando muito. sistemas obscuros.

Os tamanhos dos vários tipos serão diferentes nos sistemas de 64 bits, mas os valores inteiros serão os mesmos.


Alguns comentaristas expressaram dúvidas de que NULL seja igual a 0, mas não seja zero. Aqui está um exemplo de programa, junto com a saída esperada em um sistema assim:

#include <stdio.h>

int main () {
    size_t ii;
    int *ptr = NULL;
    unsigned long *null_value = (unsigned long *)&ptr;
    if (NULL == 0) {
        printf ("NULL == 0\n"); }
    printf ("NULL = 0x");
    for (ii = 0; ii < sizeof (ptr); ii++) {
        printf ("%02X", null_value[ii]); }
    printf ("\n");
    return 0;
}

Esse programa pode imprimir:

NULL == 0
NULL = 0x00000001
John Millikin
fonte
2
OP foi perguntando sobre '\ 0' (o caractere NUL), não '0' (o personagem zero)
Chris Lutz
2
@ Chris: '\ 0' não é NULL, é byte 0 codificado em octal em um caractere literal.
3119 John Millikin
2
Em C ++, o padrão garante que a conversão do valor inteiro 0 em um ponteiro sempre produzirá um ponteiro nulo. No C ++, é garantido que 0 é um ponteiro nulo, enquanto, por outro lado, NULL é uma macro e um codificador malicioso pode redefini-la como algo diferente.
David Rodríguez - dribeas 18/08/09
6
E NULL é garantido para ser 0. O padrão de bits de um ponteiro NULL não é garantido para ser todos os zeros, mas a constante NULL é, e sempre será, 0.
jalf
2
Sua primeira frase está errada - NULL não pode ser definido como (vazio *) 0 em C ++ porque não há conversão implícita de um vazio * em outro ponteiro (diferente de C).
-2

(void *) 0 é NULL e '\ 0' representa o final de uma string.

shinxg
fonte