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

En C, parece haber diferencias entre varios valores de cero -- NULL, NUL y 0.

Sé que el carácter ASCII '0'se evalúa como48o0x30`.

El puntero NULL se define normalmente como:

#define NULL 0

O

#define NULL (void *)0

Además, existe el carácter NUL '\0' que parece evaluarse también como 0.

¿Hay veces que estos tres valores no pueden ser iguales?

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

Solución

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


Punteros nulos

La constante entera literal 0 tiene diferentes significados dependiendo del contexto en el que se utilice. En todos los casos, sigue siendo una constante entera con el valor 0, sólo que se describe de diferentes maneras.

Si un puntero está siendo comparado con la constante literal 0, entonces es una comprobación para ver si el puntero es un puntero nulo. Este 0 es entonces referido como una constante de puntero nulo. El estándar de C define que 0 convertido al tipo void * es tanto un puntero nulo como una constante de puntero nulo.

Además, para ayudar a la legibilidad, la macro NULL se proporciona en el fichero de cabecera stddef.h. Dependiendo de su compilador puede ser posible #undef NULL y redefinirlo a algo extraño.

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

if (pointer == NULL)

NULL se define para comparar igual a un puntero nulo. Se define en la implementación cuál es la definición real de NULL, siempre y cuando sea una constante válida de puntero nulo.

if (pointer == 0)

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

if (!pointer)

Esta sentencia if comprueba implícitamente que "no es 0", así que lo invertimos para que signifique "es 0".

Las siguientes son formas INVÁLIDAS de comprobar un puntero nulo:

int mynull = 0;

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 doblan constantemente el 0 en la sentencia 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 la arquitectura subyacente. Si la arquitectura subyacente tiene un valor de puntero nulo definido como la dirección 0xDEADBEEF, entonces es el compilador el que tiene que resolver este lío.

Por lo tanto, incluso en esta curiosa arquitectura, las siguientes formas siguen siendo válidas para comprobar un puntero nulo:

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

Las siguientes son formas INVÁLIDAS de comprobar un puntero nulo:

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

ya que estas son vistas por un compilador como comparaciones normales.

Caracteres nulos

El carácter "0" se define como un carácter nulo, es decir, un carácter con todos los bits 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 la cadena está apuntando a un carácter nulo

if (*string_pointer)

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

No hay que confundirlos con los punteros nulos. Sólo porque la representación de bits es la misma, y esto permite algunos casos convenientes de cruce, no son realmente la misma cosa.

Además, `'|0'es (como todos los literales de caracteres) una constante entera, en este caso con el valor cero. Así que "0" es completamente equivalente a una constante entera "0" 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

Ver Pregunta 5.3 de las FAQ de comp.lang.c para más información. Ver este pdf para el estándar C. Consulta la sección 6.3.2.3 Punteros, párrafo 3.

Comentarios (25)

"NUL" no es 0, sino que se refiere al carácter ASCII NUL. Al menos, así es como he visto que se utiliza. El puntero nulo suele definirse como 0, pero esto depende del entorno en el que se ejecute y de la especificación del sistema operativo o del lenguaje que se utilice.

En ANSI C, el puntero nulo se especifica como el valor entero 0. Así que cualquier mundo en el que esto no sea cierto no es compatible con ANSI C.

Comentarios (0)

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

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

No recuerdo si los compiladores de C están obligados a usar ASCII - si no, '0' podría no ser siempre igual a 48. En cualquier caso, es poco probable que se encuentre con un sistema que utilice un conjunto de caracteres alternativo como EBCDIC, a menos que trabaje en sistemas muy 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 la duda de que NULL sea igual a 0, pero que no sea cero. Aquí hay un programa de ejemplo, junto con la salida esperada en tal sistema:

#include 

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
Comentarios (22)