2010-04-08 8 views
30

Recuerdo vagamente haber leído sobre esto hace un par de años, pero no puedo encontrar ninguna referencia en la red.¿Cuándo fue la macro NULL no 0?

¿Puede darme un ejemplo donde la macro NULL no se expandió a 0?

Editar para mayor claridad: Hoy se expande a ((void *)0), (0), o (0L). Sin embargo, hubo arquitecturas 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 una máquina de este tipo.

actualización para resolver los problemas:

Yo no quería decir esta pregunta en el contexto de las normas actuales, o molestar a la gente 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 punteros nulos en el estándar actual, vea this question.

+1

'NULL' no se expande a una dirección. ¿Realmente está preguntando cuándo se definió la macro 'NULL' a otra cosa, o quiere preguntar cuándo fue la representación subyacente de la constante del puntero nulo no todos los bits cero? – jamesdlin

+0

La última vez que lo vi, podría expandirse a ((nulo *) (3L - 2L - 1L)), ((((0) * 0x55)))), o ('\ 0'). (Eso está en C. Algunos de estos no estarían permitidos en C++.) –

+1

La respuesta que usted aceptó no responde a su pregunta, como se le preguntó. Corrija la pregunta (si eso no es lo que quería preguntar) o vuelva a aceptar otra respuesta. Los detalles están en los comentarios. – AnT

Respuesta

30

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

De The C FAQ List, question 5.17:

Q: En serio, tiene cualquier máquinas reales no nulos nulos realmente utilizado punteros, o diferentes representaciones para los punteros a diferentes tipos ?

A: La serie Prime 50 utilizó el segmento 07777, offset 0 para el puntero nulo , al menos para PL/I. Los modelos posteriores utilizaron el segmento 0, la compensación 0 para punteros nulos en C, necesitando nuevas instrucciones como TCNP (Prueba C Null Pointer), evidentemente como un complemento para [nota al pie] todos los códigos mal escritos que hicieron suposiciones incorrectas . Anteriormente, las máquinas Prime dirigidas a la palabra también eran notorias por requerir punteros de bytes más grandes (char * 's) que los punteros de palabra (int *' s).

serie The Eclipse MV de datos generales tiene tres formatos arquitectónicamente soportados puntero (Word, bytes, y los punteros bits), dos de los cuales son utilizados por los compiladores de C: Punteros bytes para char * y void *, y la palabra punteros para todo lo demás. Por razones históricas durante la evolución de la línea de MV de 32 bits de la línea Nova de 16 bits, los punteros y punteros de palabra tenían los bits de protección offset, indirecta y de anillo en diferentes lugares de la palabra. Al pasar un formato de puntero no coincidente a una función, se produjeron fallas de protección. Finalmente, el compilador MV C agregó muchas opciones de compatibilidad para probar para tratar con código que tenía errores de discrepancia de tipo de puntero.

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

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: las máquinas de complemento a usan una palabra de todos los bits como un indicador especial para todo tipo de datos, incluyendo direcciones no válidas.

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

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

Dependiendo del modelo `` memoria '' en uso, los procesadores 8086-familia (PC compatibles) pueden utilizar punteros de 16 bits de datos y de función punteros de 32 bits, o viceversa.

Algunas máquinas de 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.

+0

+1 para encontrar los ejemplos sólidos. FWIW, creo que ISO C ahora ordena que 0 en el código fuente significa puntero nulo sin importar la representación subyacente utilizada. Pero eso es de memoria y no tengo una copia de C1x flotando por el momento. – paxdiablo

+0

Exactamente lo que estaba buscando. Gracias. –

+5

En el código fuente C, la macro NULL todavía sería una expresión constante entera que evalúa a 0 o un molde (void *) del mismo.El valor del puntero resultante puede tener una representación que no sea 0, pero eso no significa que el código fuente pueda suponer que será 0xffff o algo así. –

2

En los compiladores de C, se puede expandir a '((void *)0)' (pero no tiene por qué hacerlo). Esto no funciona para los compiladores de C++.

Consulte también la C Preguntas frecuentes que tiene un capítulo completo en null pointers.

2

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

Algunas plataformas (ciertas máquinas CDC o Honeywell) tenían un patrón diferente de bits para NULL (es decir, no todos ceros) aunque ISO/ANSI fija que antes de C90 fue ratificado, especificando que 0 era el puntero NULL correcto en el código fuente, independientemente del patrón de bits subyacente. De C11 6.3.2.3 Pointers /4 (aunque, como se ha mencionado, esta redacción va todo el camino de regreso a C90):

Un entero expresión constante con el valor 0, o una expresión tal fundido a escribir void *, se llama una constante puntero nulo.

0

NULL macro en C se expande a la constante definida null-puntero de 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 la historia C estándar cuando NULL se expandió a algo específicamente no 0, a menos que considere (void *) 0 como "not 0". Pero (void *) 0 para NULL es ampliamente utilizado en la actualidad.

+3

Nunca ha habido un momento en la historia de _ISO_ C ... los primeros compiladores tenían patrones de bits diferentes mucho antes de que el 0 se codificara como NULO, independientemente de los bits subyacentes. Aunque, teniendo en cuenta mi avanzada edad, no hay forma de que pudiera recordar cuáles eran :-) – paxdiablo

+0

Capítulo y versículo por favor? El estándar ISO C explícitamente dice que NULL puede expandirse a lo que la implementación quiera expandir. '4.1.5. Definiciones comunes [...] Las macros son NULL, que se expande a una constante de puntero nulo definido por la implementación; y [...] '. Esto no debe confundirse con otro texto que dice que la expresión 0 convertida en tipo de puntero siempre es una forma válida de obtener un puntero nulo constante. Que no tiene nada que ver a lo que NULL se expande. – janks

+0

@janks, creo que @paxdiablo está diciendo que en ISO C, '0' en contextos de puntero es la constante de puntero nulo, pero en C pre-ISO (pre-ANSI), esto no era necesariamente cierto. Es de suponer que en esas variantes uno escribiría 'NULL', o el número mágico que fuera el puntero nulo. –

2

En el libio de GNU.h archivo:

#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 compilació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.

0

En la C moderna, 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 se estableció. 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 antiguo compilador, la plataforma podría haber definido NULL como:

#define NULL ((void*)0xFFFFF000) 

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

+0

'En el pasado, este significado formal de "0" en un contexto de puntero no estaba establecido.' - a la derecha, donde 'En el pasado' significa 'Antes de la invención del lenguaje C'. –

+0

@Windows: no, ese no es el caso. No se estableció a partir de K & R C, primera edición. 'void * ptr = 0;' no tiene un significado definido. –

+1

A mi leal saber y entender, no había ningún "vacío" o "vacío *" a partir de K & R C, primera edición. Sin embargo, el significado formal de "0" en un contexto de puntero se estableció al principio de C en 1970. –

0

compiladores de C suelen utilizar ((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 compiladores 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 especial nullptr_t que se puede convertir implícitamente en cualquier tipo de puntero, pero no en número. Esto resuelve tanto el problema de argumento variódico como el molde implícito y, adicionalmente, problemas más severos con la selección de sobrecarga (0 por razones obvias selecciona int sobrecarga sobre el puntero uno). Es legal definirlos usted mismo para compiladores anteriores y algunos compiladores de C++ lo intentaron en el pasado.

Cuestiones relacionadas