2009-05-27 6 views
52

La norme C permet aux pointeurs de différents types d'avoir des tailles différentes, par ex. sizeof(char*) != sizeof(int*) est autorisé. Il exige toutefois que si un pointeur est converti en void* et qu'il soit ensuite converti en son type d'origine, il doit être comparable à sa valeur d'origine. Par conséquent, il suit logiquement que sizeof(void*) >= sizeof(T*) pour tous les types T, correct? Sur la plupart des plates-formes courantes (x86, PPC, ARM et variantes 64 bits, etc.), la taille de tous les pointeurs est égale à la taille du registre natif (4 ou 8 octets), quel que soit le point taper. Existe-t-il des plates-formes ésotériques ou intégrées dans lesquelles les pointeurs vers différents types peuvent avoir des tailles différentes? Je suis spécifiquement à propos de données pointeurs, mais je serais également intéressé de savoir s'il existe des plates-formes où pointeurs fonction ont des tailles inhabituelles.Existe-t-il des plates-formes où les pointeurs vers différents types ont des tailles différentes?

Je suis définitivement et non je me suis renseigné sur les pointeurs-membres et les fonctions pointeur-vers-membre de C++. Ceux-ci prennent des tailles inhabituelles sur des plates-formes communes et peuvent même varier au sein d'une plate-forme, selon les propriétés de la classe pointeur-vers (non-polymorphe, héritage unique, héritage multiple, héritage virtuel ou type incomplet).

+0

Pourriez-vous poster cette section? – JaredPar

+1

Nit-pick: le "type intégral natif" en C doit être int, ce qui est rarement 64 bits, même sur les plates-formes 64 bits, AFAIK. En d'autres termes, LP64 est plus commun que ILP64. – unwind

+0

@JaredPar: Je ne sais pas exactement où il est dit dans la norme, mais cette page http://www.lysator.liu.se/c/rat/d9.html#4-9-6-1 fait mention en ce qui concerne le spécificateur de format% p fprintf. @unwind: s/taille de l'entier natif/taille du registre natif/ –

Répondre

44

Answer from the C FAQ:

La série premier 50 segment de 07.777 utilisé, décalage 0 pour le pointeur nul, au moins pour PL/I. Les modèles ultérieurs ont utilisé le segment 0, décalage 0 pour les pointeurs N en C, nécessitant de nouvelles instructions telles que TCNP (Test C Null Pointer), évidemment comme un sop à tout le code C mal écrit existant qui a fait des suppositions incorrectes. Les machines principales plus anciennes, adressées par un mot, étaient également connues pour exiger des pointeurs d'octets plus grands (char * 's) que des pointeurs de mots (int *' s). La série Eclipse MV de Data General a trois formats de pointeurs pris en charge par l'architecture (mots, octets et pointeurs), dont deux sont utilisés par les compilateurs C: pointeurs d'octets pour char * et void *, et pointeurs de mots pour tout autre. Pour des raisons historiques au cours de l'évolution de la ligne MV à 32 bits de la ligne Nova à 16 bits, les pointeurs de mots et les pointeurs d'octets avaient les bits de décalage, d'indirection et de protection d'anneau à différents endroits du mot. Le passage d'un format de pointeur non adapté à une fonction a entraîné des erreurs de protection. Finalement, le compilateur MV C a ajouté de nombreuses options de compatibilité pour essayer de traiter le code qui avait des erreurs d'incompatibilité de type pointeur.

Certaines unités centrales Honeywell-Bull utilisent le modèle de bits 06000 pour les pointeurs NULL (internes).

La série CDC Cyber ​​180 est dotée de pointeurs de 48 bits composés d'une bague, d'un segment et d'un décalage. La plupart des utilisateurs (dans l'anneau 11) ont des pointeurs NULL de 0xB00000000000. Il était courant sur les anciennes machines CDC à complément un d'utiliser un mot de tout-un-bit comme indicateur spécial pour toutes sortes de données, y compris les adresses invalides.

L'ancienne série HP 3000 utilise un schéma d'adressage différent pour les adresses octet que pour les adresses de mot; comme plusieurs des machines ci-dessus, elle utilise donc des représentations différentes pour les pointeurs char * et void * que pour les autres pointeurs.

La machine Lisp de Symbolics, une architecture étiquetée, n'a même pas de pointeurs numériques conventionnels; il utilise la paire (en gros un handle inexistant) en tant que pointeur C null.

Selon le `` modèle de mémoire '' en cours d'utilisation, processeurs 8086-famille (PC) peuvent utiliser Compatibles des pointeurs de données 16 bits et 32 ​​bits fonction pointeurs, ou vice versa.

Certaines machines Cray 64 bits représentent int * dans les 48 bits inférieurs d'un mot ; char * utilise en outre certains des 16 bits supérieurs pour indiquer une adresse dans un mot. Liens supplémentaires: A message from Chris Torek avec plus de détails sur certaines de ces machines.

+2

+1 Pourquoi n'est-ce pas la réponse acceptée? .. – dcow

+0

@David Cowden Suspect cette réponse a été écrite 4 mois après celle acceptée. Puisque le PO a demandé un exemple et l'a obtenu, cette réponse a été acceptée. Ce poste, certainement plus complet, méritait sa cote de vote élevée. Peut-être que des réponses non acceptées hautement appréciées méritent que la communauté «accepte la dérogation»? Cela ressemble à une question "méta" cependant. – chux

+0

@chux probablement mieux de simplement informer le demandeur qu'une réponse à leur question a largement dépassé la réponse acceptée et les laisser reconsidérer. En fin de compte, c'est au demandeur de déterminer quelle réponse répond le mieux à sa question. J'ai simplement commenté pour souligner le fait que j'apprécie beaucoup cette réponse (= – dcow

11

De retour dans les années dorées du DOS, des 8088 et de la mémoire segmentée, il était courant de spécifier un «modèle de mémoire» dans lequel, par ex. tout le code entrerait dans 64k (un segment) mais les données pourraient s'étendre sur plusieurs segments; cela signifiait qu'un pointeur de fonction serait de 2 octets, un pointeur de données de 4 octets. Je ne sais pas si quelqu'un est encore en train de programmer des machines de ce genre, peut-être que certaines survivent encore à des utilisations intégrées.

+0

Ils ne sont pas si rares dans le monde embarqué. DOS est encore beaucoup utilisé. –

+1

@Nils, j'ai récemment (bien après que j'avais posté cela) interviewé un nouveau diplômé (EE) et son expérience d'assemblage principal (à partir d'utilisations intégrées, bien sûr) avéré être avec Intel 8051 et Freescale 6811 - 8 bits Les descendants de processeurs J'ai étudié au collège dans les années 70 (!), et même alors nous avons rêvé de plus puissants tels que Zilog Z80. Donc, 8088 et DOS serait un grand pas là-haut ...! –

28

Pas tout à fait ce que vous demandez, mais dans les jours DOS/Windows 16 bits, vous avez fait la distinction entre un pointeur et un pointeur éloigné, ce dernier étant 32 bits.

je pourrais avoir la syntaxe mal ...

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

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

Sortie:

pInt: 2, fpInt 4

+4

Bah, j'ai complètement oublié les pointeurs proches et lointains. J'étais au courant de leur existence, mais quand j'écrivais cette question, ils m'ont totalement échappé. –

+4

Est-ce que DOS 16 bits est supposé être un exemple avec un compilateur conforme standard? – sellibitze

+0

@sellibitze: Vous avez raison - l'attribut 'far' n'est pas dans un standard C, donc l'extrait dans la réponse n'est pas valide standard C. Ainsi, cela ne répond sans doute pas tout à fait à la question (qui semble poser sur la norme C) - mais c'est toujours une réponse précieuse à mon humble avis. – sleske

7

On pourrait facilement imaginer une machine d'architecture de Harvard ayant des tailles différentes pour des pointeurs de fonction et tous les autres pointeurs. Ne sait pas d'un exemple ...

+0

Harvard Architecture utilisée souvent dans les processeurs embarqués (PIC) en 2013. – chux

+0

Oui, les architectures de Harvard apparaissent dans beaucoup de puces intégrées. Mais connaissez-vous celui qui implémente des pointeurs de fonction qui sont d'une taille différente des autres pointeurs sur la même plate-forme? – dmckee

+2

PIC16 (compilateur CCS) utilisé une goofy 9-10 bits RAM (1-2 page page reg et 8 bits de décalage.) Cumuleuse à même 'memcpy()'. Les données non-volatiles sont bloquées dans la ROM (je pense que l'adresse paire de 14-16 bits pointe vers 1 octet) et utilisent un spécial 'memcpy()/strcpy()'. Les fonctions sont difficiles à obtenir/utiliser via un pointeur de fonction, ont également l'adresse de 14-16 bits dans un demi-mot d'instruction de 14 bits, donc l'adresse doit être paire. Bien sûr, j'ai eu cette histoire foiré car j'utilise beaucoup plus souvent un PIC24 et je laisse le compilateur gérer les adresses en sachant que moi, le codeur, je ne dois pas mélanger les types de pointeurs. – chux

13

Par conséquent, il suit logiquement que sizeof(void*) >= sizeof(T*) pour tous les types T, correct? Cela ne veut pas nécessairement dire que sizeof concerne la représentation de stockage et que tous les modèles de bits ne doivent pas être des valeurs valides. Je pense que vous pourriez écrire une implémentation conforme où sizeof(int*) == 8, sizeof(void*) == 4, mais il n'y a pas plus de 2^32 valeurs possibles pour un int *. Je ne sais pas pourquoi vous voudriez.

+7

Cela dépend de votre définition de «logiquement», v) – Potatoswatter

+0

Comment 'malloc()' pourrait-il être utilisé pour allouer, puis affecter, un 'int *'? Aussi, vouliez-vous dire "Il n'y a pas plus de 2^32 valeurs possibles pour un _int * _" ou "pour un int"? – smci

+0

@smci: Je voulais dire 'int ''.Le point est que, à condition qu'il n'y ait pas plus de '2^32' valeurs légales pour un' int * ', peu importe la taille de' int * ', ou quelles sont les valeurs légales, l'implémentation peut implémente toujours la conversion de 'int *' en un' void * 'de 4 octets et vice-versa. Vous pouvez allouer puis affecter un 'int *' en utilisant 'malloc' comme suit:' int ** ppint = malloc (sizeof (* ppint)); * ppint = 0; '. Mais je ne vois pas ce que cela a à voir avec la question ou ma réponse. –

7

Des pointeurs proches et éloignés sont toujours utilisés sur certains microcontrôleurs intégrés avec pagination flash ou RAM, pour vous permettre de pointer vers la même page (pointeur proche) ou une autre page (pointeur éloigné, plus grand car inclus informations sur la page). Par exemple, le microcontrôleur HCS12 de Freescale utilise une architecture Von Neumann de 16 bits, ce qui signifie qu'aucune adresse ne peut dépasser 16 bits. En raison de la limitation que cela mettrait sur la quantité d'espace de code disponible, il y a un registre de page de 8 bits.Par conséquent, pour pointer vers des données dans la même page de code, il vous suffit de spécifier l'adresse 16 bits; c'est un pointeur proche.

Pour pointer vers des données dans une autre page de codes, vous devez inclure à la fois le numéro de page 8 bits et l'adresse 16 bits dans cette page, ce qui donne un pointeur éloigné de 24 bits.

6

Il est possible que la taille des pointeurs vers les données diffère des pointeurs vers les fonctions par exemple. Il est courant que cela se produise dans un microprocesseur pour un système embarqué. Les machines d'architecture de Harvard, comme dmckee mentionné, rendent cela facile à réaliser.

Il s'avère que ça fait mal à backcounts de gcc de se développer!:)

Edit: Je ne peux pas entrer dans les détails de la machine spécifique dont je parle mais laissez-moi ajouter pourquoi les machines Harvard rendent cela facile. L'architecture de Harvard a des capacités de stockage et des chemins différents pour les instructions et les données, donc si le bus pour les instructions est plus grand que celui pour les données, vous aurez forcément un pointeur de fonction dont la taille est plus grande qu'un pointeur! Curieux, quelle section de la norme permet les différentes tailles de pointeur?

Questions connexes