Qu'est-ce qui sont proches, loin et énormes pointeurs?

est-ce que n'importe qui peut m'expliquer ces pointeurs avec un exemple approprié ... et quand ces pointeurs sont utilisés?

27
demandé sur bolov 2010-08-26 17:36:11

6 réponses

dans les temps anciens, selon le manuel Turbo C, Un pointeur de proximité était seulement 16 bits quand votre code entier et les données s'adaptent dans un segment. Un pointeur lointain était composé d'un segment ainsi que d'un offset mais aucune normalisation n'a été effectuée. Et un énorme pointeur est automatiquement réglée. Deux pointeurs loin pourraient éventuellement pointer vers le même endroit dans la mémoire mais être différents alors que les pointeurs énormes normalisés pointant vers le même endroit de mémoire seraient toujours égaux.

12
répondu PP. 2010-08-26 13:40:51

l'exemple principal est L'architecture Intel X86.

L'Intel 8086 était, en interne, un processeur 16 bits: tous ses registres avaient 16 bits de large. Cependant, le bus d'adresse était de 20 bits de large (1 MiB). Cela signifie que vous ne pouviez pas tenir une adresse complète dans un registre, vous limitant à la première 64 kiB.

la solution D'Intel était de créer des "registres de segments" de 16 bits dont le contenu serait déplacé de quatre bits à gauche et ajouté à l'adresse. Par exemple:

DS ("Data Segment") register:  1234 h
DX ("D eXtended") register:   + 5678h
                              ------
Actual address read:           179B8h

cela a créé le concept de segment 64 kiB. Ainsi, un pointeur" près "ne serait que le contenu du registre DX (5678h), et serait invalide à moins que le registre DS ne soit déjà réglé correctement, tandis qu'un pointeur" loin " serait 32 bits (12345678h, DS suivi de DX) et fonctionnerait toujours (mais serait plus lent puisque vous deviez charger deux registres puis restaurer le registre DS une fois fait).

(comme le note supercat ci-dessous, un décalage à DX qui débordait serait "roll over" avant être ajouté à la DS pour obtenir l'adresse finale. Cela permettait aux offsets 16 bits d'accéder à n'importe quelle adresse dans le segment 64 kiB, et pas seulement à la partie qui était à ± 32 KiB d'où DX pointait, comme c'est le cas dans d'autres architectures avec des adresses 16 bits d'offset relatif dans certaines instructions.)

cependant, notez que vous pouvez avoir deux pointeurs "loin" qui sont des valeurs différentes mais pointent vers la même adresse. Par exemple, l' le pointeur far 100079B8h pointe au même endroit que 12345678h. Ainsi, la comparaison des pointeurs sur les pointeurs éloignés était une opération non valide: les pointeurs pouvaient différer, mais pointer toujours au même endroit.

c'est là que j'ai décidé que les Macs (avec Motorola 68000 processeurs à l'époque) n'étaient pas si mauvais après tout, donc j'ai manqué d'énormes pointeurs. IIRC, ils étaient juste des pointeurs loin qui garantissaient que tous les bits se chevauchant dans les registres de segment étaient 0, comme dans le deuxième exemple.

Motorola n'a pas eu ce problème avec leur série de processeurs 6800, car ils étaient limités à 64 kiB, quand ils ont créé l'architecture 68000, ils sont allés directement à 32 registres de bits, et donc jamais eu besoin de proche, loin, ou des pointeurs énormes. (Au lieu de cela, leur problème était que seuls les 24 derniers bits de l'adresse comptaient réellement, de sorte que certains programmeurs (notoirement Apple) utiliseraient les 8 bits élevés comme "indicateurs de pointeur", causant des problèmes lorsque les bus d'adresse étendue à 32 bits (4 GiB).)

Linus Torvalds juste tenu jusqu'à ce que le 80386, qui a offert un "mode protégé" où les adresses étaient 32 bits, et les registres de segment étaient la moitié supérieure de l'adresse, et aucun ajout n'était nécessaire, et a écrit Linux dès le début pour utiliser le mode protégé seulement, pas de truc de segment bizarre, et c'est pourquoi vous n'avez pas proche et loin pointeur soutien dans Linux (et pourquoi aucune entreprise concevoir une nouvelle architecture ne sera jamais revenir à eux si ils veulent Le support de Linux). Et ils ont mangé les Ménestrels de Robin, et il y avait beaucoup de joie. (Yay...)

28
répondu Mike DeSimone 2017-12-06 04:29:46

différence entre pointeurs lointains et énormes:

Comme nous le savons, par défaut, les pointeurs sont near par exemple: int *p est un near pointeur. La taille du pointeur near est de 2 octets dans le cas d'un compilateur 16 bits. Et nous savons déjà très bien que la taille varie d'un compilateur à l'autre; ils stockent seulement le décalage de l'adresse le pointeur auquel il renvoie. Une adresse constituée uniquement d'un offset a une plage de 0-64K octet.

Far et huge pointeurs:

Les pointeurs

Far et huge ont une taille de 4 octets. Ils stockent à la fois le segment et le décalage de l'adresse à laquelle le pointeur renvoie. Alors qu'est-ce que la différence entre eux?

Limitation de l'indicateur de distance:

nous ne pouvons pas changer ou modifier l'adresse de segment de l'adresse far donnée en y appliquant toute opération arithmétique. C'est en utilisant les opérateurs arithmétiques nous est impossible de passer d'un segment à l'autre segment.

si vous incrémentez l'adresse far au-delà de la valeur maximale de son adresse offset au lieu d'incrémenter l'adresse segment, elle répétera son adresse offset dans l'ordre cyclique. Cela s'appelle aussi "wrapping", c'est-à-dire que si offset est 0xffff et nous ajoutons 1 alors il est 0x0000 et de même, si nous diminuons 0x0000 de 1, alors il est 0xffff et rappelez-vous qu'il n'y a pas de changement dans le segment.

maintenant je vais comparer des pointeurs énormes et lointains:

1.Quand un pointeur lointain est incrémenté ou décrémenté seulement l'offset du pointeur est en fait incrémenté ou décrémenté mais en cas de pointeur énorme la valeur de segment et d'offset changera.

considérons L'exemple suivant, tiré de ici :

 int main()
    {
    char far* f=(char far*)0x0000ffff;
    printf("%Fp",f+0x1);
    return 0;
  }

puis la sortie est:

0000:0000

il n'y a pas de changement dans la valeur du segment.

et en cas de pointeurs énormes:

int main()
{
char huge* h=(char huge*)0x0000000f;
printf("%Fp",h+0x1);
return 0;
}

la sortie est:

0001:0000

c'est en raison de l'opération d'incrément non seulement la valeur offset, mais la valeur du segment change également.Que le segment des moyens ne changera pas dans le cas des pointeurs far mais dans le cas du pointeur huge , il peut se déplacer d'un segment à un autre .

2.Lorsque des opérateurs relationnels sont utilisés sur des pointeurs far, seuls les offsets sont comparés.En d'autres termes, les opérateurs relationnels ne travailleront sur les pointeurs far que si les valeurs de segment des pointeurs comparés sont les mêmes. Et en cas de grand cela n'arrivera pas, en fait la comparaison des adresses absolues.Laissez-nous comprendre avec l'aide d'un exemple de far pointeur :

int main()
{
char far * p=(char far*)0x12340001;
char far* p1=(char far*)0x12300041;
if(p==p1)
printf("same");
else
printf("different");
return 0;
}

sortie:

different

Dans huge pointeur :

int main()
{
char huge * p=(char huge*)0x12340001;
char huge* p1=(char huge*)0x12300041;
if(p==p1)
printf("same");
else
printf("different");
return 0;
}

sortie:

same

explication: comme nous voyons l'adresse absolue pour les deux p et p1 est 12341 ( 1234*10+1 ou 1230*10+41 ) mais ils ne sont pas considérés égaux dans le premier cas parce que dans le cas de far pointeurs seuls les offsets sont comparés i.e. il vérifiera si 0001==0041 . Ce qui est faux.

et dans le cas de pointeurs énormes, l'opération de comparaison est effectuée sur des adresses absolues qui sont égales.

  1. un pointeur lointain n'est jamais normalisé, mais un pointeur huge est normalisé . Normaliser pointeur, est celui qui a autant d'adresse que possible dans le segment, ce qui signifie que le décalage est jamais plus de 15.

    supposons que si nous avons 0x1234:1234 alors la forme normalisée de celui-ci est 0x1357:0004 (adresse absolue est 13574 ). Un pointeur énorme est normalisé seulement quand une certaine opération arithmétique est effectuée sur elle, et non normalisé pendant l'affectation.

     int main()
     {
      char huge* h=(char huge*)0x12341234;
      char huge* h1=(char huge*)0x12341234;
      printf("h=%Fp\nh1=%Fp",h,h1+0x1);
      return 0;
     }
    

    sortie:

    h=1234:1234
    
    h1=1357:0005
    

    explication: huge pointeur n'est pas normalisé en cas de cession.Mais si une opération arithmétique est réalisé sur elle, il sera normalisé.Donc, h est 1234:1234 et h1 est 1357:0005 qui est normalisé.

    4.Le décalage du pointeur énorme est inférieur à 16 en raison de la normalisation et pas tellement dans le cas des pointeurs lointains.

    prenons un exemple pour comprendre ce que je veux dire :

     int main()
      {
      char far* f=(char far*)0x0000000f;
      printf("%Fp",f+0x1);
      return 0;
      }
    

sortie:

    0000:0010

en cas de huge pointeur:

      int main()
      {
      char huge* h=(char huge*)0x0000000f;
        printf("%Fp",h+0x1);
        return 0;
        }

        Output:
        0001:0000

explication: comme nous incrémentons pointeur de loin par 1 il sera 0000:0010 .Et comme nous incrémentons énorme pointeur par 1 alors il sera 0001:0000 parce qu'il est décalé ne peut pas être plus grand que 15 en d'autres termes il sera normalisé.

19
répondu Vikas Verma 2013-11-06 14:33:22

tous les éléments de cette réponse ne concernent que l'ancien modèle 8086 et 80286.

near: un pointeur 16 bits qui peut traiter n'importe quel octet dans un segment 64k

far: pointeur 32 bits qui contient un segment et un offset. Notez que parce que les segments peuvent se chevaucher, deux pointeurs far différents peuvent pointer vers la même adresse.

énorme: un pointeur 32 bits dans lequel le segment est "normalisé" de sorte que pas deux loin les pointeurs pointent vers la même adresse sauf s'ils ont la même valeur.

tee: une boisson avec de la confiture et du pain.

qui nous ramènera à doh oh oh oh

et quand ces pointeurs sont utilisés?

dans les années 1980 et 90 'jusqu'à ce que les fenêtres 32 bits deviennent omniprésentes,

3
répondu JeremyP 2010-08-26 14:04:24

cette terminologie a été utilisée dans les architectures à 16 bits.

dans les systèmes à 16 bits, les données étaient divisées en segments de 64Kb. Chaque module chargeable (fichier de programme, bibliothèque chargée dynamiquement, etc.) avait un segment de données associé - qui pouvait stocker jusqu'à 64 Ko de données seulement.

un pointeur proche était un pointeur avec un stockage de 16 bits, et se référait aux données (seulement) dans le segment de données des modules actuels.

16bit programmes qui ont eu plus de 64Kb de données comme une exigence pourrait accéder aux allocateurs spéciaux qui retourneraient un pointeur FAR-qui était un id de segment de données dans les 16 bits supérieurs, et un pointeur dans ce segment de données, dans les 16 bits inférieurs.

pourtant, des programmes plus importants voudraient traiter plus de 64ko de données contiguës. Un pointeur énorme ressemble exactement à un pointeur lointain - il a un stockage de 32 bits - mais l'allocator a pris soin d'organiser une gamme de segments de données, avec des identifiants consécutifs, de sorte que par simplement l'incrémentation du sélecteur de segment de données le prochain segment de 64 ko de données peut être atteint.

les standards linguistiques C et C++ sous - jacents n'ont jamais vraiment reconnu ces concepts officiellement dans leurs modèles de mémoire-tous les pointeurs D'un programme C ou C++ sont censés avoir la même taille. Ainsi, les attributs proches, lointains et énormes étaient des extensions fournies par les divers fournisseurs de compilateurs.

2
répondu Chris Becke 2010-08-26 13:52:38

dans certaines architectures, un pointeur qui peut pointer vers chaque objet du système sera plus grand et plus lent à travailler qu'un pointeur qui peut pointer vers un sous-ensemble utile de choses. De nombreuses personnes ont donné des réponses concernant l'architecture x86 16 bits. Divers types de pointeurs étaient communs sur les systèmes 16 bits, bien que des distinctions de proximité/peur pourraient réapparaître dans les systèmes 64 bits, selon la façon dont ils sont mis en œuvre (Je ne serais pas surpris si de nombreux systèmes de développement vont à des pointeurs 64 bits pour tout, malgré le fait que, dans de nombreux cas, ce sera très le gaspillage).

dans de nombreux programmes, il est assez facile de subdiviser l'utilisation de la mémoire en deux catégories: les petites choses qui ensemble totalisent jusqu'à une assez petite quantité de choses (64K ou 4GB) mais seront accessibles souvent, et les choses plus grandes qui peuvent totaliser jusqu'à une quantité beaucoup plus grande, mais qui ne doivent pas être accessibles si souvent. Lorsqu'une application doit fonctionner avec une partie d'un objet dans la zone "large things", elle copie cette partie de la zone" petites choses", fonctionne avec elle, et si nécessaire écrit en arrière.

certains programmeurs se plaignent d'avoir à faire la distinction entre la mémoire" proche "et" loin", mais dans de nombreux cas faire de telles distinctions peut permettre aux compilateurs de produire beaucoup mieux de code.

(note: même sur de nombreux systèmes 32 bits, certaines zones de mémoire sont accessibles directement sans instructions supplémentaires, tandis que d'autres ne le sont pas. Si, par exemple, sur un 68000 ou un bras, un conserve un registre pointant vers le stockage global des variables, il sera possible de charger directement n'importe quelle variable dans les premiers 32K (68000) ou 2K (ARM) de ce registre. Récupérer une variable stockée ailleurs exigera une instruction supplémentaire pour calculer l'adresse. Placer des variables plus fréquemment utilisées dans les régions préférées et en informer le compilateur permettrait une génération de code plus efficace.

2
répondu supercat 2010-08-26 16:18:56