2009-05-27 11 views
52

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

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 digitar. ¿Hay alguna plataforma esotérica o incrustada en la que los punteros a diferentes tipos puedan tener diferentes tamaños? Estoy preguntando específicamente sobre datos punteros, aunque también me gustaría saber si hay plataformas donde función punteros tienen tamaños inusuales.

Definitivamente no 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).

+0

Curioso, ¿qué sección de la norma permite los diferentes tamaños de puntero? ¿Le importaría publicar esa sección – JaredPar

+1

Nit-pick: el "tipo integral nativo" en C tiene que ser int, que rara vez es de 64 bits, incluso en plataformas de 64 bits, AFAIK. En otras palabras, LP64 es más común que ILP64. – unwind

+0

@JaredPar: No estoy exactamente seguro de dónde dice eso en el estándar, pero esta página http://www.lysator.liu.se/c/rat/d9.html#4-9-6-1 hace mención de ello, con respecto al especificador de formato% p fprintf. @unwind: s/tamaño entero nativo/tamaño de registro nativo/ –

Respuesta

44

Answer from the C FAQ:

La serie Prime 50 utiliza 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 soportados arquitectónicamente (palabra, byte y punteros de bit), dos de los cuales son utilizados por compiladores C: punteros para char * y void * y punteros para todo má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 compilador 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 utilizan el patrón de bits 06000 para los punteros nulos (internos).

El CDC cibernético 180 serie ha punteros de 48 bits que consiste en un anillo, segmento, y offset. 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 vieja serie HP 3000 utiliza un esquema de direccionamiento diferente para direcciones de byte que para las 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.

El Symbolics Lisp Máquina, una arquitectura de etiquetado, ni siquiera tiene punteros numéricos convencionales; usa el par (básicamente un identificador inexistente) 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.

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

Otros enlaces: A message from Chris Torek con más detalles acerca de algunas de estas máquinas.

+2

+1 ¿Por qué esta no es la respuesta aceptada? .. – dcow

+0

@David Cowden Sospeche que esta respuesta fue escrita 4 meses después de la aceptada. Como el OP pidió un ejemplo y lo obtuvo, esa respuesta fue aceptada. Esta publicación, ciertamente más completa, merecía su alta calificación de votos. ¿Tal vez las respuestas no aceptadas altamente calificadas merecen que la comunidad "acepte anular"? Sin embargo, eso suena como una pregunta "meta". – chux

+0

@chux probablemente sea mejor notificar al solicitante que la respuesta a su pregunta ha superado ampliamente la respuesta aceptada y que los reconsidere. En última instancia, depende del solicitante determinar qué respuesta responde mejor a su pregunta. Simplemente comenté para subrayar el hecho de que * muy * disfruto mucho esta respuesta (= – dcow

11

Detrás en los años dorados de DOS, 8088s y memoria segmentada, era común especificar un "modelo de memoria" en el cual v.gr. todo el código encajaría en 64k (un segmento) pero los datos podrí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á programando para máquinas de ese tipo, tal vez algunos todavía sobreviven en usos integrados.

+0

No son tan raros en el mundo integrado. DOS todavía se usa mucho. –

+1

@Nils, recientemente (mucho después de haber publicado esto) entrevisté a un nuevo graduado (EE) y su experiencia en ensamblaje principal (desde usos integrados, por supuesto) resultó ser Intel 8051 y Freescale 6811 - 8 bits descendientes de CPUs que estudié en la universidad en los años 70 (!), e incluso entonces nos preocupamos por los más poderosos como Zilog Z80. ¡Así que 8088 y DOS serían un gran paso allá arriba ...! –

28

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.

que podría tener la sintaxis equivocada ...

int *pInt = malloc(sizeof(int)); 
int far *fpInt = _fmalloc(sizeof(int)); 

printf("pInt: %d, fpInt: %d\n", sizeof(pInt), sizeof(fpInt)); 

Salida:

Pint: 2, 4 fpInt

+4

Bah, me olvidé por completo de punteros cercanos y lejanos. Estaba al tanto de su existencia, pero cuando escribía esta pregunta, se olvidaron por completo. –

+4

¿Se supone que el DOS de 16 bits es un ejemplo con un compilador estándar? – sellibitze

+0

@sellibitze: Tiene razón: el atributo 'far' no está en ningún estándar C, por lo que el fragmento en la respuesta no es válido como estándar C. Por lo tanto, esto posiblemente no responda la pregunta (lo que parece preguntar sobre el estándar C) - pero sigue siendo una respuesta valiosa en mi humilde opinión. – sleske

7

Uno podría imaginar una máquina de arquitectura Harvard tener diferentes tamaños para los punteros de función y todos los demás indicadores. No sé de un ejemplo ...

+0

Arquitectura de Harvard utilizada a menudo en procesadores integrados (PIC) en 2013. – chux

+0

Sí, las arquitecturas de Harvard aparecen en muchos chips incrustados. Pero, ¿sabe de uno que implementa punteros de función que son de un tamaño diferente al de los otros punteros en la misma plataforma? – dmckee

+2

PIC16 (compilador CCS) usó una ridícula memoria de 9-10 bits (registro de página de 1-2 bit y desplazamiento de 8 bit.) Engorroso incluso para 'memcpy()'. Los datos no volátiles están atascados en la ROM (creo que la dirección par de 14-16 bits apunta a 1 byte) y usó un 'memcpy()/strcpy()' especial. Las funciones son difíciles de obtener/usar a través de un puntero de función, también tienen la dirección de 14-16 bits en una palabra de instrucción de media de 14 bits, por lo que la dirección debe ser par. Claro que he tenido esta historia en mal estado ya que uso un PIC24 con mucha más frecuencia y dejo que el compilador maneje las direcciones, sabiendo que yo, el codificador, no debo mezclar astutamente o no con los tipos de punteros. – chux

13

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

Esto no necesariamente sigue, ya que sizeof es sobre la representación de almacenamiento, y no todos los patrones de bits tienen que ser valores válidos. Creo que podría 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.

+7

Depende de su definición de "lógicamente"; v) – Potatoswatter

+0

¿Cómo podría utilizarse 'malloc()' para asignar, y luego asignar, un 'int *'? Además, ¿quiso decir "No hay más de 2^32 valores posibles para un _int * _" o "para un int"? – smci

+0

@smci: Quise decir 'int *'.El punto es que siempre que no haya más de '2^32' valores legales para un' int * ', entonces no importa cuál sea el tamaño de' int * ', o qué valores son los legales, la implementación puede todavía implementar la conversión de 'int *' a un 'void *' de 4 bytes y viceversa. Puede asignar y luego asignar un 'int *' usando 'malloc' de la siguiente manera:' int ** ppint = malloc (sizeof (* ppint)); * ppint = 0; '. Pero no veo qué tiene que ver ni con la pregunta ni con mi respuesta. –

7

Los punteros cercanos y lejanos todavía se utilizan 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 arquitectura 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.

Así que para apuntar a los datos en la misma página de códigos, que acaba de especificar la dirección de 16 bits; este es un puntero cercano.

para apuntar a los datos en otra página de códigos, que tiene que incluir tanto el número de página de 8 bits y la dirección de 16 bits dentro de esa página, lo que resulta en más puntero de 24 bits.

6

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 arquitectura de Harvard como dmckee mencionado hacen que esto sea fácil de lograr.

Resulta que hace backends gcc 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 arquitectura 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!

Cuestiones relacionadas