Question

Le modèle de mémoire de C, avec son utilisation de l'arithmétique des pointeurs et tout, semble modéliser un espace d'adressage plat.Les ordinateurs 16 bits utilisaient un accès mémoire segmenté.Comment les compilateurs C 16 bits ont-ils résolu ce problème et simulé un espace d'adressage plat du point de vue du programmeur C ?Par exemple, à peu près quelles instructions en langage assembleur le code suivant serait-il compilé sur un 8086 ?

long arr[65536];  // Assume 32 bit longs.
long i;
for(i = 0; i < 65536; i++) {
    arr[i] = i;
}
Était-ce utile?

La solution

  

Comment avez-compilateurs C 16 bits traitent   cette question et de simuler une adresse plate   l'espace du point de vue de la C   programmeur?

Ils ne l'ont pas. Ils ont plutôt fait la segmentation visible au programmeur C, l'extension de la langue en ayant plusieurs types de pointeurs: near, far et huge. Un pointeur de near a un seul décalage, tandis que des pointeurs de far et huge étaient un segment combiné et le décalage. Il y avait une option de compilateur pour définir le modèle de mémoire de la , qui détermine si le type de pointeur par défaut était proche ou lointain.

Dans le code Windows, même aujourd'hui, vous verrez souvent typedefs comme LPCSTR (pour const char*). Le « LP » est un vestige de l'époque 16 bits; il signifie "pointeur long (loin)".

Autres conseils

modèle de mémoire de C ne signifie en aucune façon implique l'espace d'adressage plat. Il n'a jamais fait. En fait, la spécification du langage C est spécialement conçu pour permettre à des espaces d'adressage non plats.

Dans la mise en œuvre plus trivial avec l'espace d'adressage segmenté, la taille du plus grand objet continu serait limitée par la taille du segment (65536 octets sur une plate-forme 16 bits). Cela signifie que size_t dans cette mise en œuvre serait de 16 bits, et que votre code simplement ne compilerait pas, puisque vous essayez de déclarer un objet qui a une taille plus grande que le maximum autorisé.

Une mise en œuvre plus complexe serait le soutien que l'on appelle énorme modèle de mémoire. Vous voyez, il n'y a vraiment pas de problème d'adressage des blocs de mémoire continue de any taille sur un modèle de mémoire segmentée, il faut juste quelques efforts supplémentaires dans Arithmétique de pointeur. Ainsi, dans le grand modèle de mémoire, la mise en œuvre rendrait ces efforts supplémentaires, ce qui rendrait le code un peu plus lent, mais en même temps permettrait d'adressage des objets de toutes tailles. Donc, votre code compilerait parfaitement bien.

Les vrais environnements 16 bits utilisent 16 pointeurs de bits qui atteignent une adresse. Les exemples incluent le PDP-11, famille 6800 (6802, 6809, 68HC11) et le 8085. Ceci est un environnement propre et efficace, tout comme une architecture simple 32 bits.

La famille 80x86 forcée sur nous un hybride 16 bits / 20 bits dans l'espace d'adressage soi-disant « mode réel » -le 8086 natif espace d'adressage. Le mécanisme habituel de traiter ce renforçait les types de pointeurs en deux types de base, near (pointeur 16 bits) et far (pointeur 32 bits). La valeur par défaut pour le code et les pointeurs de données peut être réglé en vrac par un « modèle de mémoire »:. tiny, small, compact, medium, far et huge (certains compilateurs ne supportent pas tous les modèles)

Le modèle de mémoire tiny est utile pour de petits programmes dans lesquels tout l'espace (code + données + de la pile) est inférieure à 64K. Tous les pointeurs sont (par défaut) 16 bits ou near; un pointeur est implicitement associé à une valeur de segment pour l'ensemble du programme.

Le modèle suppose que les données small + pile est inférieure à 64 K et dans le même segment; le segment de code contient du code seul, ne peut donc avoir jusqu'à 64K ainsi, pour un encombrement de la mémoire maximale de 128K. pointeurs de code sont near et implicitement associés à CS (le segment de code). Les données sont aussi des pointeurs near et associé à DS (le segment de données).

Le modèle de medium a jusqu'à 64 Ko de données + pile (comme faible), mais peut avoir une quantité de code. pointeurs de données sont 16 bits et sont implicitement liés au segment de données. pointeurs de code sont 32 pointeurs far bits et ont une valeur de segment en fonction de l'éditeur de liens a mis en place les groupes de codes (tracas de la comptabilité dégueu).

Le modèle de compact est le complément du milieu: moins de 64 Ko de code, mais toute quantité de données. pointeurs de données sont des pointeurs de far et le code sont near.

Dans large ou modèle huge, le sous-type par défaut des pointeurs sont 32 bits ou far. La principale différence est que d'énormes pointeurs sont toujours normalisées automatiquement afin que les incrémenter évite les problèmes avec 64K contournements wrap. Voir cette .

Sous DOS 16 bits, je ne me souviens pas d'avoir pu faire ça.Vous pourriez avoir plusieurs éléments qui faisaient chacun 64 Ko (octets) (car le segment pouvait être ajusté et le décalage mis à zéro), mais ne vous souvenez pas si vous pouviez franchir la frontière avec un seul tableau.L'espace mémoire plat où vous pouviez bon gré mal gré allouer ce que vous vouliez et atteindre aussi profondément que vous le souhaitiez dans un tableau ne s'est pas produit jusqu'à ce que nous puissions compiler des programmes DOS 32 bits (sur des processeurs 386 ou 486).Peut-être que d'autres systèmes d'exploitation et compilateurs autres que Microsoft et Borland pourraient générer des tableaux plats supérieurs à 64 Ko.Win16 Je ne me souviens pas de cette liberté jusqu'à ce que Win32 arrive, peut-être que ma mémoire rouille... De toute façon, vous étiez chanceux ou riche d'avoir un mégaoctet de mémoire, une machine de 256 Ko ou 512 Ko n'était pas rare.Votre lecteur de disquette avait finalement une fraction de Mo à 1,44 Mo, et votre disque dur, le cas échéant, en avait une douzaine ou quelques Mo, donc vous n'avez tout simplement pas calculé quelque chose d'aussi gros aussi souvent.

Je me souviens du défi particulier que j'ai eu en apprenant le DNS lorsqu'on pouvait télécharger l'intégralité de la base de données DNS de tous les noms de domaine enregistrés sur la planète. En fait, il fallait mettre en place son propre serveur DNS, ce qui était presque nécessaire à l'époque pour avoir un site Web. site.Ce fichier faisait 35 Mo, et mon disque dur faisait 100 Mo, plus Dos et Windows en mâchaient une partie.Il avait probablement 1 ou 2 Mo de mémoire, il aurait peut-être pu faire des programmes DOS 32 bits à l'époque.En partie si je voulais analyser le fichier ascii, ce que j'ai fait en plusieurs passes, mais à chaque passe, la sortie devait aller vers un autre fichier, et j'ai dû supprimer le fichier précédent pour avoir de la place sur le disque pour le fichier suivant.Deux contrôleurs de disque sur une carte mère standard, un pour le disque dur et un pour le lecteur de CD-ROM, là encore ce truc n'était pas bon marché, il n'y avait pas beaucoup d'emplacements isa de rechange si vous pouviez vous permettre un autre disque dur et une autre carte contrôleur de disque.

Il y avait même le problème de lire 64 Ko avec C, vous transmettiez le nombre d'octets que vous vouliez lire dans un entier de 16 bits, ce qui signifiait 0 à 65535 et non 65536 octets, et les performances diminuaient considérablement si vous ne lisiez pas dans des secteurs de taille égale, donc vous il suffit de lire 32 Ko à la fois pour maximiser les performances, 64 Ko n'est arrivé que bien dans les jours dos32 lorsque vous avez finalement été convaincu que la valeur transmise à fread était maintenant un nombre de 32 bits et que le compilateur n'allait pas couper les 16 bits supérieurs et seulement utilisez les 16 bits inférieurs (ce qui arrivait souvent si vous utilisiez suffisamment de compilateurs/versions).Nous rencontrons actuellement des problèmes similaires lors de la transition de 32 bits à 64 bits, comme lors de la transition de 16 à 32 bits.Ce qui est le plus intéressant, c'est le code de gens comme moi qui ont appris que le passage de 16 à 32 bits int changeait de taille, mais pas unsigned char et unsigned long, donc vous avez adapté et rarement utilisé int pour que vos programmes se compilent et fonctionnent pour à la fois 16 et 32 ​​bits.(Le code des gens de cette génération se démarque des autres personnes qui l'ont également vécu et ont utilisé la même astuce).Mais pour la transition 32 vers 64, c'est l'inverse et le code non refactorisé pour utiliser les déclarations de type uint32 en souffre.

En lisant la réponse de Wallyk qui vient d'arriver, l'énorme pointeur qui s'enroule autour sonne une cloche, et ne peut pas non plus toujours compiler pour un énorme.petit était le modèle à mémoire plate avec lequel nous sommes à l'aise aujourd'hui, et comme aujourd'hui, c'était facile car vous n'aviez pas à vous soucier des segments.Il était donc souhaitable de compiler petit quand vous le pouviez.Vous n’aviez toujours pas beaucoup de mémoire, d’espace disque ou de disquette, vous ne traitiez donc normalement pas de données aussi volumineuses.

Et d'accord avec une autre réponse, la compensation de segment était 8088/8086 Intel.Le monde entier n'était pas encore dominé par Intel, il existait donc d'autres plates-formes qui disposaient simplement d'un espace mémoire plat, ou utilisaient d'autres astuces, peut-être matérielles (en dehors du processeur), pour résoudre le problème.En raison du segment/offset, Intel a pu utiliser le 16 bits plus longtemps qu'il n'aurait probablement dû le faire.Le segment/offset offrait des choses sympas et intéressantes que vous pouviez faire avec, mais c'était aussi pénible qu'autre chose.Soit vous avez simplifié votre vie et vécu dans un espace mémoire plat, soit vous vous êtes constamment préoccupé des limites des segments.

vraiment mettre le doigt sur la taille de l'adresse sur l'ancienne de x86 est un peu délicat. On pourrait dire que son 16 bits, car l'arithmétique vous pouvez effectuer sur une adresse doit tenir dans un registre 16 bits. On pourrait aussi dire qu'il est 32 bits, car les adresses réelles sont calculées sur un registre à usage général 16 bits et registre de segment de 16 bits (tous les 32 bits sont significatifs). On pourrait aussi dire que c'est 20 bits, car les registres de segment sont décalés 4 bits gauche et ajoutés aux registres gp pour le matériel d'adressage.

Il n'a pas d'importance tant que ça qui l'un de ces vous avez choisi, car ils sont à peu près égales toutes les approximations de la machine abstraite c. Certains compilateurs vous permettent de choisir un modèle de mémoire que vous utilisez par compilation, tandis que d'autres simplement supposer 32 adresses bits, puis vérifier soigneusement que les opérations qui pourraient déborder 16 bits émettent des instructions qui poignée ce cas correctement.

Consultez cette entrée de wikipedia . A propos des pointeurs Far. Fondamentalement, il est possible d'indiquer un segment et un décalage, permettant de passer à un autre segment.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top