¿Cuál es la diferencia entre NULL, '\ 0' y 0?

309

En C, parece haber diferencias entre varios valores de cero NULL, NULy 0.

Sé que el carácter ASCII se '0'evalúa como 48o 0x30.

El NULLpuntero generalmente se define como:

#define NULL 0

O

#define NULL (void *)0

Además, está el NULpersonaje '\0'que parece evaluar 0también.

¿Hay momentos en que estos tres valores no pueden ser iguales?

¿Es esto también cierto en los sistemas de 64 bits?

gnavi
fuente
1
Consulte stackoverflow.com/questions/176989/… para obtener más información sobre las diferencias entre 0 y NULL.
David Rodríguez - dribeas
77
El identificador NULno existe en el lenguaje estándar C o en la biblioteca (o en C ++, que yo sepa). El carácter nulo a veces se llama NUL, pero a C o C ++ generalmente se le conoce como '\0'.
Keith Thompson

Respuestas:

351

Nota: Esta respuesta se aplica al lenguaje C, no a C ++.


Punteros nulos

El entero constante literal 0tiene diferentes significados según el contexto en el que se utiliza. En todos los casos, sigue siendo una constante entera con el valor 0, solo se describe de diferentes maneras.

Si se compara un puntero con el literal constante 0, entonces esta es una verificación para ver si el puntero es un puntero nulo. Esto 0se conoce como una constante de puntero nulo. El estándar C define que la 0conversión al tipo void *es tanto un puntero nulo como una constante de puntero nulo.

Además, para ayudar a la legibilidad, la macro NULLse proporciona en el archivo de encabezado stddef.h. Dependiendo de su compilador, podría ser posible #undef NULLy redefinirlo en algo loco.

Por lo tanto, aquí hay algunas formas válidas de verificar un puntero nulo:

if (pointer == NULL)

NULLse define para comparar igual a un puntero nulo. Es la implementación definida cuál es la definición real de NULL, siempre que sea una constante de puntero nulo válida.

if (pointer == 0)

0 es otra representación de la constante de puntero nulo.

if (!pointer)

Esta ifafirmación verifica implícitamente "no es 0", por lo que revertimos que significa "es 0".

Las siguientes son formas NO VÁLIDAS de verificar un puntero nulo:

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

Para el compilador, esto no es una comprobación de un puntero nulo, sino una comprobación de igualdad en dos variables. Esto podría funcionar si mynull nunca cambia en el código y las optimizaciones del compilador constantemente doblan el 0 en la instrucción if, pero esto no está garantizado y el compilador tiene que producir al menos un mensaje de diagnóstico (advertencia o error) de acuerdo con el Estándar C.

Tenga en cuenta que lo que es un puntero nulo en el lenguaje C. No importa en la arquitectura subyacente. Si la arquitectura subyacente tiene un valor de puntero nulo definido como dirección 0xDEADBEEF, entonces depende del compilador resolver este desorden.

Como tal, incluso en esta arquitectura divertida, las siguientes formas siguen siendo formas válidas de verificar un puntero nulo:

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

Las siguientes son formas NO VÁLIDAS de verificar un puntero nulo:

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

ya que estos son vistos por un compilador como comparaciones normales.

Personajes nulos

'\0'se define como un carácter nulo, es decir, un carácter con todos los bits puestos a cero. Esto no tiene nada que ver con los punteros. Sin embargo, puede ver algo similar a este código:

if (!*string_pointer)

comprueba si el puntero de cadena apunta a un carácter nulo

if (*string_pointer)

comprueba si el puntero de cadena apunta a un carácter no nulo

No los confundas con punteros nulos. Solo porque la representación de bits es la misma, y ​​esto permite algunos casos cruzados convenientes, en realidad no son lo mismo.

Además, '\0'es (como todos los literales de caracteres) una constante entera, en este caso con el valor cero. Por '\0'lo tanto, es completamente equivalente a una 0constante entera sin adornos : la única diferencia está en la intención que transmite a un lector humano ("Estoy usando esto como un carácter nulo").

Referencias

Consulte la pregunta 5.3 de las preguntas frecuentes de comp.lang.c para obtener más información. Vea este pdf para el estándar C. Consulte las secciones 6.3.2.3 Punteros, párrafo 3.

Andrew Keeton
fuente
3
Gracias por señalar la lista de preguntas frecuentes. Sin embargo, consulte también c-faq.com/null/nullor0.html
Sinan Ünür
44
No, no se comparará ptrcon todos los bits cero . Esto no es un memcmp, pero es una comparación usando un operador incorporado. Un lado es una constante de puntero nulo '\0', y el otro lado es un puntero. Igual que con las otras dos versiones con NULLy 0. Esos tres hacen lo mismo.
Johannes Schaub - litb
66
Está tomando el operador de comparación integrado como una cosa que compararía cadenas de bits. Pero eso no es lo que es. Compara dos valores, que son conceptos abstractos. Por lo que un puntero nulo que internamente se representa como 0xDEADBEEFsigue siendo un puntero nulo, no importa lo que les gusta su aspecto cadena de bits, y todavía se comparará igual a NULL, 0, \0y todas las demás formas constantes de puntero nulo.
Johannes Schaub - litb
2
Haces un buen punto sobre el operador de comparación. Repasé sobre C99. Dice "Una expresión constante entera con el valor 0, o una expresión de ese tipo para escribir void *, se llama constante de puntero nulo". También dice que un carácter literal es una expresión constante entera. Por lo tanto, por la propiedad transitiva tienes razón en eso ptr == '\0'.
Andrew Keeton
2
"... podría ser posible #undef NULL y redefinirlo en algo loco. Cualquiera que haga esto merece ser fusilado". este mi buen señor me hizo reír a carcajadas ...
oggiemc
34

Parece que varias personas no entienden cuáles son las diferencias entre NULL, '\ 0' y 0. Entonces, para explicar, y en un intento de evitar repetir cosas que dije antes:

Una expresión constante de tipo intcon el valor 0, o una expresión de este tipo, convertir a tipo void *es una constante de puntero nulo , que si se convierte en un puntero se convierte en un puntero nulo . Está garantizado por el estándar comparar desigual a cualquier puntero a cualquier objeto o función .

NULLes una macro, definida como una constante de puntero nulo .

\0es una construcción utilizada para representar el carácter nulo , utilizado para terminar una cadena.

Un carácter nulo es un byte que tiene todos sus bits establecidos en 0.

amaterasu
fuente
14

Los tres definen el significado de cero en diferentes contextos.

  • contexto del puntero: se utiliza NULL y significa que el valor del puntero es 0, independientemente de si es 32 bits o 64 bits (un caso de 4 bytes y los otros 8 bytes de ceros).
  • contexto de cadena: el carácter que representa el dígito cero tiene un valor hexadecimal de 0x30, mientras que el carácter NUL tiene un valor hexadecimal de 0x00 (utilizado para terminar cadenas).

Estos tres siempre son diferentes cuando miras la memoria:

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

Espero que esto lo aclare.

Nasko
fuente
8
Nasko: Evalúa sizeof('\0')y sorpréndete.
caf
3
@Nasko: Estaba realmente sorprendido: con gcc, en C: sizeof ('\ 0') == sizeof ('a') == 4, mientras que con g ++, en C ++: sizeof ('\ 0') == sizeof ('a') == 1
David Rodríguez - dribeas
1
@Nasko: Del estándar C (borrador, n1124): 'Una constante de caracteres enteros tiene el tipo int', por lo tanto '\ 0' es en realidad del tipo int en C, y por lo tanto sizeof ('\ 0') es 4 en mi arquitectura (Linux, 32 bits)
David Rodríguez - dribeas
@dribeas: no lo describía como una constante, más bien como lo que verías como parte de la cadena. Definitivamente podría haberlo hecho explícito. Gracias
Nasko
@ DavidRodríguez-dribeas deshizo editar " '0' valor ASCII corregido a 0x20 (desc 32)"
Chux - Restablecer Monica
6

Si NULL y 0 son equivalentes como constantes de puntero nulo, ¿qué debo usar? en la lista de preguntas frecuentes de C también se aborda este problema:

Los programadores de C deben entender eso NULLy 0son intercambiables en contextos de puntero, y que una difusión 0 es perfectamente aceptable. Cualquier uso de NULL (en oposición a 0) debe considerarse un recordatorio suave de que hay un puntero involucrado; los programadores no deberían depender de él (ya sea por su propia comprensión o la del compilador) para distinguir los punteros 0de los enteros 0.

Es solo en contextos de puntero que NULLy 0son equivalentes. NULLno debe usarse cuando 0se requiere otro tipo de , aunque pueda funcionar, porque al hacerlo envía un mensaje estilístico incorrecto. (Además, ANSI permite la definición de NULLser ((void *)0), que no funcionará en absoluto en contextos sin puntero). En particular, no utilice NULLcuando NULse desea el carácter nulo ASCII ( ). Proporcione su propia definición

#define NUL '\0'

si debes.

Sinan Ünür
fuente
5

¿Cuál es la diferencia entre NULL, '\ 0' y 0

"carácter nulo (NUL)" es más fácil de descartar. '\0'Es un carácter literal. En C, se implementa como int, por lo tanto, es lo mismo que 0, que es de INT_TYPE_SIZE. En C ++, el literal de caracteres se implementa como char, que es 1 byte. Esto normalmente es diferente de NULLo 0.

A continuación, NULLhay un valor de puntero que especifica que una variable no apunta a ningún espacio de direcciones. Deje de lado el hecho de que generalmente se implementa como ceros, debe poder expresar el espacio de direcciones completo de la arquitectura. Por lo tanto, en una arquitectura de 32 bits, NULL (probable) es de 4 bytes y en la arquitectura de 64 bits de 8 bytes. Esto depende de la implementación de C.

Finalmente, el literal 0 es de tipo int, que es de tamaño INT_TYPE_SIZE. El valor predeterminado de INT_TYPE_SIZEpodría ser diferente según la arquitectura.

Apple escribió:

El modelo de datos de 64 bits utilizado por Mac OS X se conoce como "LP64". Este es el modelo de datos común utilizado por otros sistemas UNIX de 64 bits de Sun y SGI, así como Linux de 64 bits. El modelo de datos LP64 define los tipos primitivos de la siguiente manera:

  • las entradas son de 32 bits
  • los largos son de 64 bits
  • long-longs también son de 64 bits
  • los punteros son de 64 bits

Wikipedia de 64 bits :

El compilador VC ++ de Microsoft usa el 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 : se agregó más en el carácter literal.

#include <stdio.h>

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

El código anterior devuelve 4 en gcc y 1 en g ++.

Eugene Yokota
fuente
2
No, no'\0' es un valor de 1 byte. Es un carácter literal, que es una expresión constante entera, por lo que si se puede decir que tiene un tamaño, entonces es el tamaño de un (que debe ser de al menos 2 bytes). Si no me crees, evalúa y comprueba por ti mismo. , y todos son completamente equivalentes. intsizeof('\0')'\0'00x0
caf
@caf depende del idioma. Si no me crees, prueba sizeof('\0')un compilador de C ++.
Eugene Yokota
2
debe usar "% zu" al imprimir sizeof (algo)
usar
4

Un NUL de un L, termina una cadena.

Un NULL de dos L no apunta a nada.

Y apostaré un toro dorado

Que no hay tres-L NULLL.

¿Cómo lidias con NUL?

EvilTeach
fuente
4

Una buena pieza que me ayuda al comenzar con C (Tomado de la Programación Expert C de Linden)

El One 'l' nul y The Two 'l' null

Memorice esta pequeña rima para recordar la terminología correcta para punteros y ASCII cero:

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. 

El carácter ASCII con el patrón de bits cero se denomina "NUL". El valor del puntero especial que significa que el puntero no apunta a ninguna parte es "NULL". Los dos términos no son intercambiables en significado.

dlmeetei
fuente
Mucho más simple: NULes un código de control, tales como BEL, VT, HT, SOTetc, y por lo tanto tiene un máximo. 3 personajes
glglgl
2

"NUL" no es 0, pero se refiere al carácter ASCII NUL. Al menos, así es como lo he visto usar. El puntero nulo a menudo se define como 0, pero esto depende del entorno en el que se esté ejecutando y la especificación de cualquier sistema operativo o idioma que esté utilizando.

En ANSI C, el puntero nulo se especifica como el valor entero 0. Por lo tanto, cualquier mundo donde eso no sea cierto no cumple con ANSI C.

peterb
fuente
1

Un byte con un valor de 0x00es, en la tabla ASCII, el carácter especial llamado NULo NULL. En C, dado que no debe incrustar caracteres de control en su código fuente, esto se representa en cadenas C con un 0 escapado, es decir,\0 .

Pero un verdadero NULL no es un valor. Es la ausencia de un valor. Para un puntero, significa que el puntero no tiene nada que señalar. En una base de datos, significa que no hay valor en un campo (que no es lo mismo que decir que el campo está en blanco, 0 o lleno de espacios).

El valor real que utiliza un determinado sistema o formato de archivo de base de datos para representar a NULLno es necesariamente 0x00.

richardtallent
fuente
0

NULLno se garantiza que sea 0; su valor exacto depende de la arquitectura. La mayoría de las arquitecturas principales lo definen (void*)0.

'\0' siempre será igual a 0, porque así es como se codifica el byte 0 en un carácter literal.

No recuerdo si se requieren los compiladores de C para uso ASCII - si no, '0'no siempre podría ser igual a 48. En cualquier caso, es poco probable que alguna vez encuentra un sistema que utiliza un juego de caracteres alternativo como EBCDIC a menos que esté trabajando muy sistemas oscuros

Los tamaños de los distintos tipos diferirán en los sistemas de 64 bits, pero los valores enteros serán los mismos.


Algunos comentaristas han expresado dudas de que NULL sea igual a 0, pero no sea cero. Aquí hay un programa de ejemplo, junto con la salida esperada en dicho sistema:

#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;
}

Ese programa podría imprimir:

NULL == 0
NULL = 0x00000001
John Millikin
fuente
2
OP estaba preguntando sobre '\ 0' (el carácter NUL), no sobre '0' (el carácter cero)
Chris Lutz
2
@Chris: '\ 0' no es NULL, es el byte 0 codificado en octal en un carácter literal.
John Millikin
2
En C ++, el estándar garantiza que la conversión del valor entero 0 a un puntero siempre producirá un puntero nulo. En C ++, se garantiza que 0 será un puntero nulo, mientras que NULL es una macro y un codificador malicioso podría redefinirlo como algo diferente.
David Rodríguez - dribeas
66
Y se garantiza que NULL será 0. El patrón de bits de un puntero NULL no se garantiza que sean todos ceros, pero la constante NULL es, y siempre será, 0.
jalf
2
Su primera oración es incorrecta: NULL no se puede definir como (void *) 0 en C ++ porque no hay conversión implícita de un void * a otro puntero (a diferencia de C).
-2

(nulo *) 0 es NULL y '\ 0' representa el final de una cadena.

shinxg
fuente