Les tableaux et les enregistrements sont susceptibles d'occuper une place importante en mémoire. Certaines notions sur son organisation s'avèrent rapidement nécessaires.
Les PC sont susceptibles d'adresser 1Mo de mémoire conventionnelle, celle qui nous intéresse ici. Les 640 premiers Ko sont utilisés par le DOS et les programmes; la partie suivante a d'autres utilisations, elle contient en particulier la mémoire vidéo.
La mémoire est organisée en blocs de 64 Ko au maximum (cela explique la taille maximale d'un tableau) qui sont appelés des segments. Pour désigner un octet de la mémoire, on indique son segment, puis la place qu'il occupe dans ce segment et qu'on appelle offset. Le numéro du segment et l'offset sont des nombres codés sur deux octets (type Word de Turbo Pascal).
Turbo pascal considère la mémoire comme un tableau spécial nommé Mem si on le considère comme tableau d'octets et MemW si on le considère comme tableau d'éléments de type Word. Pour avoir accès à un élément de mémoire on utilise l'une des expressions Mem[segment:offset] ou MemW[segment:offset].
Par exemple :
Note : il est dangereux d'utiliser cette possibilité lorsqu'on ne sait pas à quoi correspond l'adresse utilisée.
Pour désigner un emplacement dans la mémoire Turbo Pascal utilise le type pointeur. Le type Pointer permet de désigner un pointeur général qui désigne une adresse de la mémoire sans préjuger de son contenu.
Il est aussi possible de définir des pointeurs sur des zones destinées à recevoir des variables d'un type prédéfini; on utilise pour cela le symbole ^ suivi du nom du type.
Par exemple, pour déclarer un type pointeur sur un entier de type Integer, on pourra écrire :
Type PointeurSurInteger = ^Integer;
Lorsqu'on dispose d'une variable de type pointeur sur un type donné, on peut avoir accès à la variable pointée en utilisant encore une fois le symbole ^ derrière le nom de la variable pointeur.
Par exemple :
Si V est une variable du type PointeurSurInteger, l'expression V^
représentera l'entier de type Integer pointé par V. Cela signifie que V^
pourra être utilisé comme n'importe quelle variable de type Integer.
Tous les pointeurs peuvent prendre une valeur spéciale représentée par le mot nil, qu'on peut traduire par nulle part.
Il est possible de savoir où Turbo Pascal a rangé une variable en utilisant les fonctions Seg et Off qui renvoient respectivement le segment et l'offset d'une variable.
Par exemple :
si une variable V de type quelconque a été déclarée, Seg(V) et Off(V)
renvoient le segment et l'offset de cette variable.
La fonction Addr, quant à elle, renvoie un pointeur de type général sur une variable.
Par exemple :
addr(V) renvoie un pointeur sur V; addr(V) peut être remplacé par @V.
Grâce au symbole ^ évoqué précédemment, les pointeurs peuvent remplacer des variables, mais ils sont moins faciles à utiliser. On peut légitimement se demander pourquoi utiliser des pointeurs alors que Turbo Pascal sait gérer les variables sans qu'on ait à se poser de questions sur leurs adresses en mémoire. Pour répondre à cette question il est nécessaire d'avoir une idée de l'organisation en mémoire d'un programme Turbo Pascal au moment où on l'exécute.
Lorsqu'il s'installe en mémoire un programme Turbo Pascal occupe plusieurs segments qui ont chacun un rôle spécifique.
Il y a d'abord les segments de code contenant les instructions destinées au micro processeur; on a un segment par unité utilisée et un segment pour le programme. Notons que cela signifie que le code d'une unité ou d'un programme ne doit pas dépasser 64 Ko. C'est, entre autres, l'une des raisons de l'existence des unités : un programme peut ainsi avoir une taille supérieure à 64 Ko.
Les données, donc les variables déclarées dans le programme et les unités, vont être stockées dans deux segments : le segment des données qui contient les variables globales et le segment de la pile qui contiendra pendant le déroulement du programme, entre autres choses, les variables locales et les paramètres utilisés par les procédures et les fonctions.
Comme un segment ne peut pas dépasser 64 Ko, l'ensemble des variables globales ne peut pas dépasser cette limite. De même, l'ensemble des variables locales utilisées à un moment donné ne peut avoir qu'une taille limitée. Nous voyons ainsi apparaître le défaut majeur des variables si commodes à utiliser. Par exemple, il sera impossible de charger en mémoire un texte de 70 Ko en n'utilisant que des variables.
Revenons aux 640 Ko que le PC offre au DOS et aux programmes. Le Dos occupe selon la configuration du système environ 50 Ko. Il reste donc 590 Ko. Il est bien rare qu'un programme, avec ses segments de code et de données occupe tout cet espace. Lorsque le programme est chargé en mémoire, il reste donc une partie importante de mémoire disponible. Cette partie est prise en charge par Turbo Pascal et se nomme le tas. C'est pour avoir accès au tas, et donc à une grande quantité de mémoire que l'utilisation de pointeurs se révèle indispensable.
La gestion de la mémoire située dans le tas se déroule selon le principe suivant : si un programme a besoin d'une partie de cette mémoire il doit la demander et lorsqu'il n'en a plus besoin il doit la libérer. Turbo Pascal nous fournit les procédures nécessaires à la réalisation de ces opérations.
Ces deux procédures permettent de gérer simplement les pointeurs sur des variables typées. La procédure New effectue la demande de mémoire dans le tas, la procédure Dispose se charge de la libération.
Syntaxe :
New(PointeurSurVariableTypée); Dispose(PointeurSurVariableTypée);
Par exemple, déclarons la variable PS de type ^String; il s'agit d'un pointeur sur une chaîne de caractères. Au moment de sa déclaration, Turbo Pascal réservera 4 octets pour PS qui ne contiendra qu'une adresse mémoire. Pour que PS pointe réellement sur une zone mémoire de 256 octets réservée à une chaîne, il faudra utiliser la procédure New. Ainsi après exécution de New(PS), PS pointera sur une zone du tas et PS^ pourra être utilisé comme n'importe quelle chaîne de caractères. Lorsque la chaîne PS^ sera devenue inutile, il suffira d'exécuter Dispose(PS) pour libérer la zone de 256 octets précédemment pointée par PS. Cette zone pourra être réutilisée à d'autres fins.
Ces deux procédures permettent une gestion plus fine de l'attribution de mémoire car elles permettent de préciser la quantité de mémoire utilisée.
Pour reprendre l'exemple précédent, GetMem(PS,256) a le même effet que New(PS). Cependant, si l'on estime que la chaîne pointée par PS n'aura pas plus de 80 caractères on pourra faire une économie de mémoire en demandant simplement GetMem(PS,81). (81 à cause de l'octet de longueur).
Lorsqu'on effectue une demande de mémoire dans le tas avec New ou GetMem il n'est pas toujours possible de la satisfaire; n'oublions pas la limite des 640 Ko du PC. D'autre part il peut être utile de connaître la quantité de mémoire nécessaire pour une variable de type donné. Deux fonctions de Turbo Pascal répondent à ces questions.
Par exemple : SizeOf(Integer) renvoie 2 et SizeOf(String) renvoie 256.