¿Hay plataformas donde los punteros a diferentes tipos tengan diferentes tamaños?

El estándar C permite punteros a diferentes tipos para tener diferentes tamaños, por ejemplo, sizeof(char*) != sizeof(int*) está permitido. Sin embargo, sí requiere que si un puntero se convierte en un void* y luego se vuelve a convertir a su tipo original, se debe comparar como igual a su valor original. Por lo tanto, se deduce lógicamente que sizeof(void*) >= sizeof(T*) para todos los tipos T , ¿correcto?

En las plataformas más comunes actualmente en uso (x86, PPC, ARM y variantes de 64 bits, etc.), el tamaño de todos los punteros es igual al tamaño del registro nativo (4 u 8 bytes), independientemente del tipo de apuntado. ¿Hay alguna plataforma esotérica o incrustada en la que los punteros a diferentes tipos puedan tener diferentes tamaños? Específicamente estoy preguntando sobre los punteros de datos , aunque también me gustaría saber si hay plataformas donde los punteros de función tengan tamaños inusuales.

Definitivamente no estoy preguntando sobre los punteros a miembros de C ++ y las funciones de puntero a miembro. Ésos toman tamaños inusuales en plataformas comunes, e incluso pueden variar dentro de una plataforma, dependiendo de las propiedades de la clase de puntero a (no polimórficos, herencia única, herencia múltiple, herencia virtual o tipo incompleto).

Respuesta de la C Preguntas frecuentes :

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 desplazamiento 0 para los punteros nulos en C, lo que exigió nuevas instrucciones como TCNP (puntero nulo de la prueba C), evidentemente como un complemento a todo el código C existente mal escrito que suponía incorrectamente. 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.

Enlaces adicionales: un mensaje de Chris Torek con más detalles sobre algunas de estas máquinas.

No es exactamente lo que estás preguntando, pero en los días de DOS / Windows de 16 bits, tenías la distinción entre un puntero y un puntero lejano, siendo este último de 32 bits.

Podría tener la syntax incorrecta …

 int *pInt = malloc(sizeof(int)); int far *fpInt = _fmalloc(sizeof(int)); printf("pInt: %d, fpInt: %d\n", sizeof(pInt), sizeof(fpInt)); 

Salida:

pInt: 2, fpInt 4

Por lo tanto, se deduce lógicamente que sizeof(void*) >= sizeof(T*) para todos los tipos T, ¿correcto?

Eso no necesariamente sigue, ya que sizeof se trata de la representación de almacenamiento, y no todos los patrones de bits tienen que ser valores válidos. Creo que podrías escribir una implementación conforme donde sizeof(int*) == 8 , sizeof(void*) == 4 , pero no hay más de 2 ^ 32 valores posibles para un int *. No estoy seguro de por qué querrías.

En los años dorados de DOS, 8088 y memoria segmentada, era común especificar un “modelo de memoria” en el cual, por ejemplo, todo el código cabía en 64k (un segmento) pero los datos podían abarcar múltiples segmentos; esto significaba que un puntero de función sería de 2 bytes, un puntero de datos, 4 bytes. No estoy seguro si alguien todavía está progtwigndo para máquinas de ese tipo, tal vez algunos todavía sobreviven en usos integrados.

Uno podría imaginarse fácilmente una máquina de architecture de Harvard con diferentes tamaños para punteros de función y todos los demás punteros. No sé de un ejemplo …

Los punteros cercanos y lejanos todavía se usan en algunos microcontroladores incorporados con memoria flash paginada o RAM, para permitirle apuntar a datos en la misma página (cerca del puntero) u otra página (puntero lejano, que es más grande porque incluye información de la página).

Por ejemplo, el microcontrolador HCS12 de Freescale utiliza una architecture de Von Neumann de 16 bits, lo que significa que ninguna dirección puede tener más de 16 bits. Debido a la limitación que esto supondría para la cantidad de espacio de código disponible, existe un registro de página de 8 bits.

Entonces, para señalar datos en la misma página de códigos, solo debe especificar la dirección de 16 bits; este es un puntero cercano.

Para señalar datos en otra página de códigos, debe incluir tanto el número de página de 8 bits como la dirección de 16 bits dentro de esa página, lo que da como resultado un puntero lejano de 24 bits.

Es posible que el tamaño de los punteros a los datos difiera de los punteros a las funciones, por ejemplo. Es común que esto ocurra en un microprocesador para un sistema integrado. Las máquinas de architecture de Harvard como dmckee mencionado hacen que esto sea fácil de lograr.

¡Resulta que hace que gcc backends sea un dolor para desarrollar! 🙂

Editar: No puedo entrar en los detalles de la máquina específica de la que estoy hablando, pero permítanme agregar por qué las máquinas de Harvard lo hacen fácil. La architecture de Harvard tiene diferentes almacenamientos y rutas a las instrucciones y los datos, por lo tanto, si el bus de las instrucciones es “más grande” que el de los datos, ¡seguramente tendrá un puntero de función cuyo tamaño es más grande que un puntero a datos!