¿Cuándo fue la macro NULL no 0?

Recuerdo vagamente haber leído sobre esto hace un par de años, pero no puedo encontrar ninguna referencia en la red.

¿Me puede dar un ejemplo donde la macro NULL no se expandió a 0?

Edite para mayor claridad: hoy se expande a ((void *)0) , (0) o (0L) . Sin embargo, hubo architectures olvidadas hace tiempo donde esto no era cierto, y NULL se expandió a una dirección diferente. Algo como

 #ifdef UNIVAC #define NULL (0xffff) #endif 

Estoy buscando un ejemplo de esa máquina.

Actualización para abordar los problemas:

No quise decir esta pregunta en el contexto de los estándares actuales, ni molestar a las personas con mi terminología incorrecta. Sin embargo, mis suposiciones fueron confirmadas por la respuesta aceptada:

Los modelos posteriores usaron [blah], evidentemente como un complemento de todo el código C existente mal escrito que hizo suposiciones incorrectas.

Para una discusión sobre apuntadores nulos en el estándar actual, vea esta pregunta .

C FAQ tiene algunos ejemplos de máquinas históricas con representaciones NULL 0.

De la lista de preguntas frecuentes de The C , pregunta 5.17 :

P: En serio, ¿alguna máquina real realmente ha utilizado punteros nulos distintos de cero, o representaciones diferentes para punteros a diferentes tipos?

A: La serie Prime 50 usó el segmento 07777, offset 0 para el puntero nulo, al menos para PL / I. Los modelos posteriores usaron el segmento 0, el offset 0 para los punteros nulos en C, necesitando nuevas instrucciones como TCNP (Test C Null Pointer), evidentemente como un complemento a [nota al pie] de todo el código C existente mal escrito que hizo suposiciones incorrectas. Las máquinas primarias antiguas y dirigidas a palabras también eran notorias por requerir punteros de bytes más grandes (char * ‘s) que los punteros de palabra (int *’ s).

La serie Eclipse MV de Data General tiene tres formatos de puntero arquitectónicos (punteros de palabra, byte y bit), dos de los cuales son utilizados por los comstackdores de C: punteros de bytes para char * y void * y punteros de palabra para todo lo demás. Por razones históricas durante la evolución de la línea de MV de 32 bits desde la línea Nova de 16 bits, los punteros de palabra y punteros de bytes tenían bits de compensación, indirección y protección de anillo en diferentes lugares de la palabra. Pasar un formato de puntero no coincidente a una función dio como resultado fallas de protección. Eventualmente, el comstackdor de MV C agregó muchas opciones de compatibilidad para intentar tratar con código que tenía errores de discrepancia de tipo de puntero.

Algunos mainframes Honeywell-Bull usan el patrón de bits 06000 para punteros nulos (internos).

La serie CDC Cyber ​​180 tiene punteros de 48 bits que constan de un anillo, segmento y desplazamiento. La mayoría de los usuarios (en el anillo 11) tienen punteros nulos de 0xB00000000000. Era común en los CDC antiguos: complementan las máquinas para usar una palabra de todos los bits como un indicador especial para todo tipo de datos, incluidas las direcciones no válidas.

La antigua serie HP 3000 usa un esquema de direccionamiento diferente para direcciones de bytes que para direcciones de palabra; al igual que varias de las máquinas de arriba, por lo tanto, utiliza diferentes representaciones para los punteros char * y void * que para otros punteros.

La máquina Symbolics Lisp, una architecture etiquetada, ni siquiera tiene punteros numéricos convencionales; usa el par (básicamente un identificador inexistente) como un puntero nulo C.

Dependiendo del “modelo de memoria” en uso, los procesadores 8086-family (PC compatibles) pueden usar punteros de datos de 16 bits y punteros de función de 32 bits, o viceversa.

Algunas máquinas Cray de 64 bits representan int * en los 48 bits más bajos de una palabra; char * usa adicionalmente algunos de los 16 bits superiores para indicar una dirección de bytes dentro de una palabra.

En los comstackdores de C, puede expandirse a ‘ ((void *)0) ‘ (pero no tiene por qué hacerlo). Esto no funciona para los comstackdores de C ++.

Consulte también las Preguntas frecuentes C, que tiene un capítulo completo sobre punteros nulos .

En el archivo GNU libio.h:

 #ifndef NULL # if defined __GNUG__ && \ (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) # define NULL (__null) # else # if !defined(__cplusplus) # define NULL ((void*)0) # else # define NULL (0) # endif # endif #endif 

Tenga en cuenta la comstackción condicional en __cplusplus. C ++ no puede usar ((void *) 0) debido a sus reglas más estrictas sobre el lanzamiento de punteros; el estándar requiere que NULL sea 0. C permite otras definiciones de NULL.

Hubo un tiempo hace mucho tiempo cuando se tipeó como ((void*)0) o alguna otra forma específica de máquina, donde esa máquina no usó el patrón de bits con cero bits.

Algunas plataformas (ciertas máquinas CDC o Honeywell) tenían un patrón de bits diferente para NULL (es decir, no para todos los ceros) aunque ISO / ANSI solucionó eso antes de que se ratificara C90, al especificar que 0 era el puntero NULL correcto en el código fuente, independientemente de el patrón de bits subyacente. De C11 6.3.2.3 Pointers /4 (aunque, como se mencionó, esta redacción se remonta a C90):

Una expresión constante entera con el valor 0 , o una expresión de este tipo para escribir void * , se llama constante de puntero nulo.

En C moderno, void *pointer = 0; está destinado a inicializar “puntero” para no apuntar a nada. Es específico de la plataforma si eso se logra al establecer los bits de “puntero” a cero.

En el pasado, este significado formal de “0” en un contexto de puntero no estaba establecido. Fue necesario configurar el puntero al valor real que la plataforma trata como “no apunta a ninguna parte”. Como ejemplo, una plataforma puede elegir una dirección fija que nunca obtiene una página asignada a ella. En este caso, en un comstackdor antiguo, la plataforma podría haber definido NULL como:

 #define NULL ((void*)0xFFFFF000) 

Por supuesto, hoy en día, no hay razón para no definirlo como ((void*)0) .

Los comstackdores C generalmente usan ((void *)0) . El motivo es pasar NULL a funciones con argumentos variables (o ahora funciones raras pero aún legales sin prototipo). Cuando los punteros son más grandes que int, 0 solo se promocionará a int y, por lo tanto, no se leerá correctamente como puntero.

Los comstackdores de C ++ no pueden usar esa definición porque C ++ no permite la conversión implícita de void * (la conversión de 0 a cualquier puntero es especial). Sin embargo, C ++ 11 introdujo la nueva palabra clave nullptr que es una constante de puntero nulo de tipo nullptr_t especial, implícitamente convertible a cualquier tipo de puntero, pero no a número. Esto resuelve tanto el problema de argumentos variados como el molde implícito y, adicionalmente, problemas más severos con la selección de sobrecarga ( 0 por razones obvias selecciona la sobrecarga int sobre el puntero uno). Es legal definirlos usted mismo para comstackdores anteriores y algunos comstackdores de C ++ lo intentaron en el pasado.

NULL macro NULL en C se expande a la constante de null-puntero definida por la implementación. Puede ser cualquier cosa (ya que está definido por la implementación), pero en el contexto del puntero el efecto es siempre el mismo que si se expandiera a la constante 0 .

Nunca ha habido un momento en el historial estándar de C cuando NULL expandido a algo específicamente distinto de 0 , a menos que considere (void *) 0 como “no 0”. Pero (void *) 0 para NULL es ampliamente utilizado hasta el día de hoy.