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.