Aller au contenu

Instruction RPG full free · IBM i (AS/400) · Mots-clés de déclaration

BASED

Zone posée sur une adresse choisie

Déclare une zone ou une structure sans mémoire propre : elle s'applique sur l'adresse contenue dans un pointeur que vous positionnez.

Syntaxe

dcl-s zone type based(pointeur);
dcl-ds nom likeds(modele) based(pointeur);
pointeur = %addr(variable);
pointeur = %alloc(taille);
dealloc(n) pointeur;
pointeur
Nom du pointeur qui contient l'adresse où la zone est lue et écrite. Si ce pointeur n'est pas déclaré, il est créé implicitement.
Effet
Aucun indicateur. La zone désigne la mémoire qui se trouve à l'adresse du pointeur : changer le pointeur change la zone vue, sans copie. Un pointeur nul fait échouer tout accès (RNQ0222).

À quoi ça sert

BASED sert à lire une même description à plusieurs endroits : un tampon reçu, un élément de liste chaînée, une zone de mémoire allouée à la demande avec %ALLOC. On déplace la fenêtre en changeant seulement le pointeur.

C'est aussi le moyen d'allouer un grand tampon de travail dont la taille n'est connue qu'à l'exécution, puis de le libérer.

Exemple 1 : Une structure vue sur deux enregistrements

**free
// BASED : une structure sans memoire propre, posee sur une adresse choisie.
ctl-opt dftactgrp(*no) actgrp(*new);

dcl-ds enreg_t qualified template;
  numcli packed(5:0);
  nom    char(15);
end-ds;

dcl-ds courant likeds(enreg_t) based(pcourant);
dcl-ds premier likeds(enreg_t) inz;
dcl-ds second  likeds(enreg_t) inz;

premier.numcli = 100;
premier.nom = 'Dupont SA';
second.numcli = 200;
second.nom = 'Martin et fils';

pcourant = %addr(premier);
snd-msg 'Courant -> ' + %char(courant.numcli) + ' ' + %trimr(courant.nom);

pcourant = %addr(second);
snd-msg 'Courant -> ' + %char(courant.numcli) + ' ' + %trimr(courant.nom);

courant.nom = 'Martin SAS';
snd-msg 'Second modifie -> ' + %trimr(second.nom);

*inlr = *on;

Résultat réel, compilé et exécuté sur IBM i 7.5 :

Courant -> 100 Dupont SA
Courant -> 200 Martin et fils
Second modifie -> Martin SAS

courant n'occupe aucune mémoire. Pointée vers premier, elle montre le client 100 ; pointée vers second, le client 200. Écrire dans courant.nom modifie second : c'est la même mémoire.

Exemple 2 : Mémoire allouée et libérée

**free
// BASED avec memoire allouee : %alloc, puis dealloc. DEALLOC seul laisse le pointeur plein.
ctl-opt dftactgrp(*no) actgrp(*new);

dcl-s zone char(12) based(p);
dcl-s liste packed(7:2) dim(3) based(pl);

p = %alloc(%size(zone));
zone = 'Montpellier';
snd-msg 'Zone : [' + zone + ']';
dealloc p;
snd-msg 'Apres DEALLOC, pointeur nul ? ' + %char(p = *null);

pl = %alloc(%size(liste));
liste(1) = 1250.50;
snd-msg 'Liste(1) : ' + %char(liste(1));
dealloc(n) pl;
snd-msg 'Apres DEALLOC(N), pointeur nul ? ' + %char(pl = *null);

*inlr = *on;

Résultat réel, compilé et exécuté sur IBM i 7.5 :

Zone : [Montpellier ]
Apres DEALLOC, pointeur nul ? 0
Liste(1) : 1250.50
Apres DEALLOC(N), pointeur nul ? 1

%alloc réserve la mémoire, dealloc la rend. Après un simple dealloc p, le pointeur contient encore l'ancienne adresse (le test à *null donne 0) ; dealloc(n) le remet à *null (1).

Exemple 3 : Pointeur nul

**free
// Piege : utiliser une zone BASED dont le pointeur est *NULL.
ctl-opt dftactgrp(*no) actgrp(*new);

dcl-s zone char(10) based(p);

snd-msg 'Pointeur nul : ' + %char(p = *null);
zone = 'Lyon';
snd-msg 'Zone : [' + zone + ']';

*inlr = *on;

Résultat réel, compilé et exécuté sur IBM i 7.5 :

Pointeur nul : 1
Erreur : RNQ0222 - (C G D F) Erreur de paramètre ou de pointeur.

Le pointeur n'a jamais été positionné : il est à *null. L'écriture dans la zone arrête le travail sur RNQ0222 (message système MCH3601, pointeur non défini).

Le piège

Une zone BASED dont le pointeur est nul fait planter le programme. L'affectation zone = 'Lyon' s'arrête sur RNQ0222 (MCH3601). Et après un dealloc simple, le pointeur n'est pas remis à *null : un test if p = *null répondrait faux alors que la mémoire est rendue. Écrivez dealloc(n) p; pour que le pointeur soit nul après la libération.