• Aucun résultat trouvé

Mode de passage de paramètre

N/A
N/A
Protected

Academic year: 2022

Partager "Mode de passage de paramètre"

Copied!
77
0
0

Texte intégral

(1)

c 2006 Marc Feeley IFT2030 page 490

Mode de passage de paramètre

Déf: mode de passage de paramètre = lien qui existe entre le paramètre formel et le paramètre actuel

Normallement le choix du mode de passage se fait indépendamment pour chaque paramètre

Les 4 modes les plus répandus

1. par valeur (existe dans presque tous les langages) 2. par référence (Pascal, Modula-2, C++)

3. par valeur-résultat (Ada) 4. par nom (Algol, SIMULA)

(2)

c 2006 Marc Feeley IFT2030 page 491

R-value et L-value (1)

Déf: une expression “R-value” dénote une donnée tel qu’un nombre, enregistrement, pointeur, etc

(c’est-à-dire l’évaluation d’une R-value produit une donnée)

Déf: Une expression “L-value” dénote un

emplacement en mémoire (c’est-à-dire l’évaluation d’une L-value produit un emplacement)

(3)

c 2006 Marc Feeley IFT2030 page 492

R-value et L-value (2)

Exemple en C: dans cet énoncé

t[x] = t[y]+1;

t[x] est une L-value

x, y, t[y], 1 et t[y]+1 sont des R-value

Note: le nom vient de la position de ce type

d’expression dans une affectation (du côté gauche

“L-value” ou du côté droit “R-value”)

(4)

c 2006 Marc Feeley IFT2030 page 493

Passage par valeur

Le paramètre actuel est une R-value

Le paramètre formel est une variable créée pour l’activation et initialisée avec la valeur du paramètre actuel

Exemple en C

int carre (int x) /* x pass´e par valeur */

{ x = x*x; return x; } void test ()

{ int n = 3;

printf ("%d", carre (n)); /* 9 */

printf ("%d", n); /* 3 */

printf ("%d", carre (n+1)); /* 16 */

}

n 3 x 3

(5)

c 2006 Marc Feeley IFT2030 page 494

Passage par référence (1)

Le paramètre actuel est une L-value, c’est-à-dire une variable ou tout autre emplacement mémoire

Le paramètre formel désigne le même emplacement mémoire que celui désigné par le paramètre actuel au moment de l’activation (i.e. c’est un alias de

l’emplacement mémoire)

(6)

c 2006 Marc Feeley IFT2030 page 495

Passage par référence (2)

Exemple en C++

int carre (int &x) /* x pass´e par r´ef´erence */

{ x = x*x; return x; } void test ()

{ int n = 3;

printf ("%d", carre (n)); /* 9 */

printf ("%d", n); /* 9 */

}

n 3 x

(7)

c 2006 Marc Feeley IFT2030 page 496

Passage par référence (3)

Le passage par valeur d’un pointeur permet d’obtenir le même effet en C

int carre (int *x) /* x = ptr. pass´e par valeur */

{ *x = (*x)*(*x); return *x; } void test ()

{ int n = 3;

printf ("%d", carre (&n)); /* 9 */

printf ("%d", n); /* 9 */

}

n 3 x

(8)

c 2006 Marc Feeley IFT2030 page 497

Passage par valeur-résultat (1)

Le paramètre actuel est une L-value, c’est-à-dire une variable ou tout autre emplacement mémoire

Le paramètre formel est une variable créée pour

l’activation et initialisée avec le contenu du paramètre actuel

Au retour de la procédure, le paramètre formel est copié dans le paramètre actuel

(9)

c 2006 Marc Feeley IFT2030 page 498

Passage par valeur-résultat (2)

Exemple en Ada

i, n : integer;

procedure add2 (x : in out integer) is begin

x := x+i;

x := x+i;

end;

n := 0; i := 1;

add2 (n);

put (n); -- imprime 2 (serait 2 par r´ef´erence) add2 (i);

put (i); -- imprime 3 (serait 4 par r´ef´erence)

Permet accès plus rapide au paramètre formel que passage par référence (évite l’indirection)

Utile pour les langages concurrents (effet externe à un moment précis)

(10)

c 2006 Marc Feeley IFT2030 page 499

Passage par nom (1)

Le paramètre actuel est une L-value ou une R-value

Une utilisation du paramètre formel évalue le paramètre actuel comme L-value (si du côté gauche d’une

affectation) sinon comme R-value

Le paramètre actuel est évalué à chaque accès et dans le contexte de l’appelant

(11)

c 2006 Marc Feeley IFT2030 page 500

Passage par nom (2)

Exemple en SIMULA

integer i, n;

integer array t(1:2);

procedure add2 (x); name x; integer x;

begin

x := x+1;

i := i+1;

x := x+1;

end;

n := 0; i := 1;

add2 (n);

outint (n,0); ! imprime 2 (serait 2 par r´ef´erence) ; n := 0; i := 1; t(1) := 0; t(2) := 0;

add2 (t(i));

outint (t(1),0); ! imprime 1 (serait 2 par r´ef´erence) ; outint (t(2),0); ! imprime 1 (serait 0 par r´ef´erence) ;

(12)

c 2006 Marc Feeley IFT2030 page 501

Passage par nom (3)

Les subtilités de la sémantique du passage par nom peuvent être exploitées pour créer des nouvelles

structures de contrôle

Exemple: calcul d’une somme générale, tel que

X10

i=1

1

2i = 1

2 + 1

4 + . . . + 1

1024

(13)

c 2006 Marc Feeley IFT2030 page 502

Passage par nom (4)

real procedure somme (index, bas, haut, val);

name index, val;

integer index, bas, haut;

real val;

begin

integer j;

real s;

s := 0;

for j := bas step 1 until haut do begin

index := j;

s := s + val;

end;

somme := s;

end;

integer i;

outfix (somme (i,1,10,1/2**i), 10, 15); ! 0.9990234375 outfix (somme (i,1,10,i), 1, 4); ! 55.0 ;

(14)

c 2006 Marc Feeley IFT2030 page 503

Noms et portée (1)

Les noms sont utilisés pour identifier des types, variables, procédures, etc. (identificateurs)

Dans la plupart des langages un même nom peut servir à désigner plusieurs chôses

Les règles de portée d’un langage spécifient ce qui est désigné par un nom, en fonction du contexte de son

utilisation

(15)

c 2006 Marc Feeley IFT2030 page 504

Noms et portée (2)

Déf: une déclaration introduit un nouveau sens pour un nom

• déclaration de type, de variable, de paramètre, de procédure, ...

• Exemple 1: int x; => déclaration de variable qui introduit le nom x

• Exemple 2: typedef signed char byte; =>

déclaration de type qui introduit le nom byte

• Exemple 3: void f (byte x) { ... } =>

déclaration de fonction et de paramètre qui introduit les noms f et x

(16)

c 2006 Marc Feeley IFT2030 page 505

Noms et portée (3)

Déf: la portée d’une déclaration = la région du

programme où l’utilisation du nom déclaré désigne cette déclaration

Exemple en C

int carre (int x) { return x*x; } int f (short x)

{ return carre (carre (x/2)); }

Portée de “int x” = corps de carre, portée de “int carre (int x)” = corps de carre et f

(17)

c 2006 Marc Feeley IFT2030 page 506

Noms et portée (4)

La portée à un certain point du programme correspond à une fonction, p.e. dans le corps de carre:

utilisation port´ee

d’un nom ---> eclaration

x int x

carre int carre (int x)

Les deux sortes de portée les plus répandues:

1. Portée lexicale (plupart des langages)

2. Portée dynamique (Perl, APL, TeX, Lisp)

(18)

c 2006 Marc Feeley IFT2030 page 507

Portée lexicale (1)

Règle: un nom désigne la déclaration englobante la plus proche textuellement

Donc, une déclaration locale à un bloc (ou procédure) a précéance, à l’intérieur de ce bloc, sur toute déclaration du même nom externe à ce bloc

(19)

c 2006 Marc Feeley IFT2030 page 508

Portée lexicale (2)

Exemple en C

char *x = "allo"; /* d´eclaration 1 de x */

void proc1 (int x, int y)/* d´eclaration 2 de x */

{ if (x > y) {

int x = 2*y; /* d´eclaration 3 de x */

printf ("%d\n", x);/* x --> d´ecl 3 */

}

printf ("%d\n", x); /* x --> d´ecl 2 */

}

void proc2 () { proc1 (3, 2);

printf ("%s\n", x); /* x --> d´ecl 1 */

}

Note: en C, une “déclaration” de variable n’inclus pas l’initialisation (i.e. la portée de “int x” débute après le

= dans “int x = x*y;”)

(20)

c 2006 Marc Feeley IFT2030 page 509

Portée lexicale (3)

Dans certains langages il est permis de déclarer une procédure dans une autre (procédures imbriquées), p.e. Pascal, Modula-2, Ada, SIMULA, Scheme, Perl mais pas C et FORTRAN

La portée des déclarations locales à une procédure s’étend aux sous-procédures

(21)

c 2006 Marc Feeley IFT2030 page 510

Portée lexicale (4)

Exemple en Pascal

procedure A;

var x : char; (* variable locale de A *) procedure B; (* proc´edure locale de A *)

begin

writeln(x);

end;

procedure C; (* proc´edure locale de A *) var x : char; (* variable locale de C *) begin x := ’C’; B; end;

begin

x := ’A’;

B; (* imprime A *) C; (* imprime A *) B; (* imprime A *) end;

(22)

c 2006 Marc Feeley IFT2030 page 511

Portée dynamique (1)

Règle: un nom désigne la déclaration englobante la plus proche dans la chaîne d’activations

Donc, si la procédure A appèle la procédure B, une déclaration qui est visible dans A le sera également dans B (à moins que B déclare le même nom)

(23)

c 2006 Marc Feeley IFT2030 page 512

Portée dynamique (2)

Exemple en Perl

sub A

{ local $x;

sub B

{ printf ("%s\n", $x);

} sub C

{ local $x = "C";

B;

}

$x = "A";

B; # imprime A C; # imprime C B; # imprime A }

Note: en Perl, l’utilisation de “my” à la place de “local” donne la portée lexicale

(24)

c 2006 Marc Feeley IFT2030 page 513

Portée dynamique (3)

En général la portée dynamique est à éviter car sa sémantique dépend de l’ordre d’exécution

Un cas où la portée dynamique est appropriée, c’est pour passer un paramètre implicite à toutes les

procédures appelées pendant l’activation courante

(25)

c 2006 Marc Feeley IFT2030 page 514

Portée dynamique (4)

Exemple en C: changer la destination d’impression

void print_char (char c, FILE* output) { fputc (c, output); }

void print_str (char* msg, FILE* output) { while (*msg != ’\0’)

print_char (*msg++, output);

print_char (’\n’, output);

}

void print_page (char** t, int n, FILE* output) { int i;

for (i=0; i<n; i++)

print_str (t[i], output);

}

char* p[3] = { "ligne 1", "ligne 2", "ligne 3" };

void reports () { FILE* fich;

print_page (p, 3, stdout);

fich = fopen ("rapport", "w");

print_page (p, 3, fich);

fclose (fich);

}

(26)

c 2006 Marc Feeley IFT2030 page 515

Portée dynamique (5)

Si C utilisait la portée dynamique:

void print_char (char c) { fputc (c, output); }

void print_str (char* msg)

{ while (*msg != ’\0’) print_char (*msg++);

print_char (’\n’);

}

void print_page (char** t, int n) { int i;

for (i=0; i<n; i++) print_str (t[i]);

}

char* p[3] = { "ligne 1", "ligne 2", "ligne 3" };

void reports () { FILE* output;

output = stdout;

print_page (p, 3);

output = fopen ("rapport", "w");

print_page (p, 3);

fclose (output);

}

(27)

c 2006 Marc Feeley IFT2030 page 516

Portée dynamique (6)

Dans un langage non-concurrent, on peut émuler la portée dynamique avec une variable globale qui est sauvegardée/restaurée à chaque point de liaison (dans un langage concurrent il y a un problème de

partage de la variable globale par plusieurs processus)

La portée lexicale se compile mieux (programmes plus rapides) que la portée dynamique car la

correspondance nom–déclaration est connue à la compilation

(28)

c 2006 Marc Feeley IFT2030 page 517

Macros et portée dynamique (1)

Le compilateur C (cc) est en fait composé de plusieurs sous-programmes qui traitent à leur tour le programme à compiler

cpp ccom as

source en C source en

C épuré

code assembleur

code machine

cc

(29)

c 2006 Marc Feeley IFT2030 page 518

Macros et portée dynamique (2)

cpp = “préprocesseur C” qui traite toutes les directives de préprocesseur “#” et les macros (le résultat est un programme C sans directives et macros)

#include "...": inclusion de fichier

#if/#ifdef ...: compilation conditionnelle

#define ...: définition de macro

Déf: une macro c’est une sorte de procédure dont

l’appel sera traité par le préprocesseur, en remplaçant l’appel par le corps de la macro avec une substitution textuelle des paramètres

(30)

c 2006 Marc Feeley IFT2030 page 519

Macros et portée dynamique (3)

Source Source apr`es pr´etraitement

#include <stdio.h> | int printf (char *format, ...);

| ... et le reste de

| /usr/include/stdio.h

#define N 10 |

#define MIN(x,y) (x<y)?x:y |

|

#if N < 5 |

int t[N]; |

#else |

int t[N*2]; | int t[10*2];

#endif |

|

int min3(int a,int b,int c)| int min3(int a,int b,int c) { a = MIN(a,b); | { a = (a<b)?a:b;

a = MIN(a,c); | a = (a<c)?a:c;

return a; | return a;

} | }

|

int main () | int main ()

{ | {

#ifdef sun |

printf ("allo SUN\n"); |

#endif |

|

#ifdef linux |

printf ("allo linux\n"); | printf ("allo linux\n");

#endif |

|

return 0; | return 0;

} | }

(31)

c 2006 Marc Feeley IFT2030 page 520

Macros et portée dynamique (4)

Les macros permettent d’éviter le coût d’activation (transferts de contrôle, copie des paramètres, ...)

L’expansion textuelle des macros peut cependant accroître la taille de l’exécutable

Note: une sorte de portée dynamique survient avec la substitution textuelle des paramètres lorsque la macro contient des déclarations

Exemple

#define swap(x,y) { int t = y; y = x; x = t; } void proc (int a, int b, int t, int t2)

{ swap(a,b); /* { int t = b; b = a; a = t; }; */

swap(t,t2); /* { int t = t2; t2 = t; t = t; }; */

/* le deuxieme appel ne change pas t et t2 */

}

(32)

c 2006 Marc Feeley IFT2030 page 521

Macros et procédures “en ligne”

(1)

Les macros ont d’autres différences sémantiques avec les procédures

Catégorie syntaxique de l’expansion 6= appel (idem pour paramètres formels)

#define abs(x) (x<0) ? -x : x

#define swap(x,y) { int t = y; y = x; x = t; }

b = abs(a+1)*2; /* b = (a+1<0) ? -a+1 : a+1*2; */

if (a < b) /* if (a < b) */

swap(a,b); /* { int t = b; b = a; a = t; }; */

else ... /* else ... <-- erreur de syntaxe */

#define abs(x) (((x)<0) ? -(x) : (x))

#define swap(x,y) \

do { int t = y; y = x; x = t; } while (0) b = (((a+1)<0) ? -(a+1) : (a+1))*2;

if (a < b)

do { int t = b; b = a; a = t; } while (0);

else ...

(33)

c 2006 Marc Feeley IFT2030 page 522

Macros et procédures “en ligne”

(2)

Évaluation multiple du paramètre actuel (ceci est un problème difficile à contourner en même temps que les conflits de noms)

b = abs(f(a));

/* b = (((f(a)+1)<0) ? -(f(a)+1) : (f(a)+1))*2; */

swap(x[i++],x[j++]);

/* do

{ int t=x[j++]; x[j++]=x[i++]; x[i++]=t; } while (0);

*/

#define swap(x,y) \ do \

{ int *px=&(x), *py=&(y), t=*py; *py=*px; *px=t; } \ while (0)

(34)

c 2006 Marc Feeley IFT2030 page 523

Macros et procédures “en ligne”

(3)

Certains langages permettent d’indiquer qu’une procédure est “en ligne”

Exemple en C++

inline void swap (int *x, int *y) { int t = *y;

*y = *x;

*x = t;

}

C’est une indication qu’il est souhaitable que le corps de cette procédure soit expansé à la place de chaque appel

Le compilateur est libre d’ignorer cette indication car le résultat du programme ne changera pas

(35)

c 2006 Marc Feeley IFT2030 page 524

Macros et procédures “en ligne”

(4)

Le compilateur doit renommer les paramètres

formels et variables locales de la procédure dans chaque expansion pour éviter les conflits de noms

int t, x;

swap(&t,&x); /* { int *v601 = &t;

int *v602 = &x;

int v603 = *v602;

*v602 = *v601;

*v601 = v603;

}

*/

(36)

c 2006 Marc Feeley IFT2030 page 525

Paramètres optionnels

Ada et C++ permettent de donner une valeur de défaut aux paramètres

Si le paramètre actuel n’est pas fourni, la valeur de défaut est utilisée

Exemple C++: affichage d’un entier dans une certaine base sur un certain fichier de sortie

void print (int n, int base = 10, FILE *f = stdout) { ... }

void proc ()

{ print (13); // decimal sur stdout print (13, 2); // binaire sur stdout FILE *fich = fopen ("xxx", "w");

print (13, 8, fich); // octal sur xxx fclose (fich);

}

(37)

c 2006 Marc Feeley IFT2030 page 526

Paramètres nommés

Ada et Lisp permettent de spécifier les paramètres actuels en les nommant (dans n’importe quel ordre)

Utile lorsque combiné avec valeurs de défaut et aussi pour documenter le programme

Exemple en Ada

procedure header (page : in Integer;

title : in String := "";

center: in Boolean := True) is begin ... end header;

procedure example is begin

header (100, "", False);

header (100, center => False);

header (center => False, page => 100);

end example;

(38)

c 2006 Marc Feeley IFT2030 page 527

Surdéfinition de procédure (1)

Ada, C++ et Prolog permettent d’utiliser un même nom pour déclarer plusieurs procédures (procédures

surdéfinies)

Le compilateur décide quelle procédure appeler en fonction du nombre et type des paramètres

La déclaration qui concorde “le mieux” avec l’appel est choisie

(39)

c 2006 Marc Feeley IFT2030 page 528

Surdéfinition de procédure (2)

Exemple en C++

void print (int n) { ... } // V.1 void print (char c) { ... } // V.2 void test ()

{

print (123); // V.1 print (’X’); // V.2 print (true); // V.1

print (33.5); // erreur car ambigu¨e }

(40)

c 2006 Marc Feeley IFT2030 page 529

Sélection de procédure surdéfinie en C++ (1)

Cas à 1 paramètre: sélection par spécificité

1. Recherche d’une procédure avec paramètre formel de même type que paramètre actuel

2. Nouvelle recherche après promotion numérique du paramètre actuel

• enum,bool,char,short,int -> int

• long -> long

• float,double -> double

3. Nouvelle recherche en considérant les conversions standard du paramètre actuel:

élargissement, p.e. char -> long

rétrécissement, p.e. float -> int

• T* -> void*

(41)

c 2006 Marc Feeley IFT2030 page 530

Cas à plus d’un paramètre

Éliminer les procédures qui sont moins spécifiques qu’une des autres sur au moins un des paramètres (eme > prom > conv)

L’appel est ambiguë s’il ne reste pas exactement une procédure

Exemple avec 2 paramètres

void f (char x, int y) { ... } // V.1 void f (int x, char y) { ... } // V.2

void test () // V.1 V.2

{f (’A’,123); // V.1 eme+mˆeme prom+conv f (123,’A’); // V.2 conv+prom eme+mˆeme f (8.7,true);// V.1 conv+prom conv+conv f (123,123); // ambigu¨e conv+mˆeme eme+conv }

(42)

c 2006 Marc Feeley IFT2030 page 531

Surdéfinition des opérateurs (1)

C++ permet de surdéfinir +, -, <<, etc

Utile pour mieux intégrer les types usagers au langage (nombres, impression)

Se fait comme une définition de fonction avec le nom

“operator<op ´erateur>” et au moins un paramètre doit être un struct ou class

(43)

c 2006 Marc Feeley IFT2030 page 532

Surdéfinition des opérateurs (2)

Exemple: implantation des nombres complexes

typedef struct cpx { double reel, imag; } cpx;

cpx operator+ (cpx a, cpx b) { cpx r;

r.reel = a.reel + b.reel;

r.imag = a.imag + b.imag;

return r;

}

cpx operator+ (cpx a, double b) { cpx t;

t.reel = b;

t.imag = 0;

return a + t;

}

void test () { cpx x, y, z;

x = (y + (1 + 2)) + z;

}

(44)

c 2006 Marc Feeley IFT2030 page 533

Activation de procédure (1)

Objectif: expliquer les mécanismes d’activation de procédure et d’accès aux variables

Problème fondamental: comment associer une valeur à une variable?

Nom de variable --> valeur de la variable

Décomposé en 3 étapes

1. Nom de var. --> déclaration de var.

règles de portée

2. Déclaration de var. --> emplacement mém.

activation courante

3. Emplacement mém. --> valeur

état de la mémoire

(45)

c 2006 Marc Feeley IFT2030 page 534

Activation de procédure (2)

La deuxième étape est nécessaire pour traiter les procédures récursives

Déf: une procédure est récursive s’il est possible que pendant une de ses activation elle soit activée à

nouveau

Directement récursive: f-->f

Indirectement récursive: f-->g-->f

(46)

c 2006 Marc Feeley IFT2030 page 535

Activation de procédure (3)

Exemple en Pascal

program recursif;

var x : integer;

function f(n:integer) : integer;

begin

if n <= 1 then f := 1

else

f := n * f(n-1);

end;

begin x := f(3); writeln(x); end.

(47)

c 2006 Marc Feeley IFT2030 page 536

Activation de procédure (4)

Il y a 3 activations de f et chaque activation a sa propre instance de la variable n

program recursif;

+---+

var x : integer; x | ? | +---+

+---+ +---+ +---+

n | 3 | n | 2 | n | 1 |

+---+ +---+ +---+

function f(n:integer) function f(n:integer) function f(n:integer)

: integer; : integer; : integer;

begin begin begin

if n <= 1 then if n <= 1 then if n <= 1 then

f := 1 f := 1 f := 1

else else else

f := n * f(n-1); f := n * f(n-1); f := n * f(n-1);

end; end; end;

begin x := f(3); writeln(x); end.

La récursivité implique l’allocation dynamique des

variables locales et paramètres formels (en général le nombre d’appels récursifs n’est pas connu

statiquement)

(48)

c 2006 Marc Feeley IFT2030 page 537

Activation: cas non-récursif (1)

L’espace pour stocker les paramètres et variables

locales des procédures non-récursives peut être alloué statiquement (p.e. FORTRAN)

(49)

c 2006 Marc Feeley IFT2030 page 538

Activation: cas non-récursif (2)

Exemple, compilation de Pascal à C (qui est utilisé comme un “langage machine” de haut niveau)

Programme Pascal C sans param`etres/r´esultat program non_recursif; |int x;

|

var x : integer; |int mult_n, mult_m, mult_res;

|

function mult(n,m:integer)|void mult()

: integer; |{

begin | mult_res = mult_n * mult_m;

mult := n*m; |}

end; |

|void non_recursif()

begin |{

x := mult(2,3); | mult_n = 2;

writeln(x); | mult_m = 3;

x := mult(4,5); | mult();

writeln(x); | x = mult_res;

end. | printf("%d",x);

|

| mult_n = 4;

| mult_m = 5;

| mult();

| x = mult_res;

| printf("%d",x);

|}

(50)

c 2006 Marc Feeley IFT2030 page 539

Activation: cas non-récursif (3)

Pour expliquer plus en détail les transferts de contrôle requis pour une activation, nous utilisons les “goto

calculés” qui existent sur certains compilateurs C (gcc)

• &&<´etiqu> => pointeur (de type void*)

goto *<ptr ´etiqu>; => saut à <´etiqu>

• Exemple

void *dest;

if (x<0) dest = &&neg; else dest = &&pos;

goto *dest;

neg: ...

pos: ...

(51)

c 2006 Marc Feeley IFT2030 page 540

Activation: cas non-récursif (4)

La procédure appelante doit indiquer à la procédure appelée où il faut retourner le contrôle après

l’activation (adresse de retour)

L’adresse de retour est simplement un paramètre implicite de toutes les procédures

(52)

c 2006 Marc Feeley IFT2030 page 541

Activation: cas non-récursif (5)

Programme Pascal C sans proc´edures program non_recursif; |int x;

|

var x : integer; |void *mult_ret;

|int mult_n, mult_m, mult_res;

function mult(n,m:integer)|

: integer; |void non_recursif()

begin |{

mult := n*m; | mult_n = 2;

end; | mult_m = 3;

| mult_ret = &&point1;

begin | goto mult;

x := mult(2,3); |point1:

writeln(x); | x = mult_res;

x := mult(4,5); | printf("%d",x);

writeln(x); |

end. | mult_n = 4;

| mult_m = 5;

| mult_ret = &&point2;

| goto mult;

|point2:

| x = mult_res;

| printf("%d",x);

|

| return;

|

|mult:

| mult_res = mult_n * mult_m;

| goto *mult_ret;

|}

(53)

c 2006 Marc Feeley IFT2030 page 542

Activation: cas récursif (1)

Ceci ne fonctionne pas avec les procédures récursives

program recursif; |int x;

|

var x : integer; |void *f_ret;

|int f_n, f_res;

function f(n:integer)|

: integer; |void recursif()

begin |{ f_n = 3; f_ret = &&point1;

if n <= 1 then | goto f;

f := 1 |point1:

else | x = f_res;

f := n * f(n-1); | printf("%d",x);

end; | return;

|

begin |f: if (f_n <= 1)

x := f(3); | f_res = 1;

writeln(x); | else

end. | { f_n = f_n-1; f_ret = &&point2;

| goto f;

| point2:

| f_res = f_n * f_res;

| }

| goto *f_ret;

|}

La valeur de f n et de f ret au retour de f sont incorrectes

(54)

c 2006 Marc Feeley IFT2030 page 543

Activation: cas récursif (2)

L’état d’une activation est conservé dans un bloc d’activation qui stocke

• Les paramètres formels (incluant l’adresse de retour)

• Les variables locales et temporaires (introduites par le compilateur)

• Le résultat de la fonction (pas vraiment nécessaire car allocation statique possible)

• Le lien dynamique qui chaîne les blocs des activations qui n’ont pas encore terminées

Le lien dynamique permet de retrouver le bloc de la procédure appelante au moment du retour de la

procédure courante

(55)

c 2006 Marc Feeley IFT2030 page 544

Activation: cas récursif (3)

program recursif; |typedef struct bloc

| { int n; void *ret; struct bloc *dyn; } bloc;

var x : integer; |

|bloc *bac, *p;

function f(n:integer)|int x;

: integer; |int f_res;

begin |

if n <= 1 then |void recursif()

f := 1 |{

else | bac = NULL;

f := n * f(n-1); |

end; | p = malloc(sizeof(bloc));

| p->n = 3; p->ret = &&point1;

begin | p->dyn = bac; bac = p;

x := f(3); | goto f;

writeln(x); |point1:

end. | p = bac->dyn; free(bac); bac = p;

| x = f_res;

| printf("%d",x);

| return;

|

|f: if (bac->n <= 1)

| f_res = 1;

| else

| { p = malloc(sizeof(bloc));

| p->n = bac->n-1; p->ret = &&point2;

| p->dyn = bac; bac = p;

| goto f;

| point2:

| p = bac->dyn; free(bac); bac = p;

| f_res = bac->n * f_res;

| }

bac = NULL | goto *bac->ret;

|}

(56)

c 2006 Marc Feeley IFT2030 page 545

Activation: cas récursif (4)

program recursif; |typedef struct bloc

| { int n; void *ret; struct bloc *dyn; } bloc;

var x : integer; |

|bloc *bac, *p;

function f(n:integer)|int x;

: integer; |int f_res;

begin |

if n <= 1 then |void recursif()

f := 1 |{

else | bac = NULL;

f := n * f(n-1); |

end; | p = malloc(sizeof(bloc));

| p->n = 3; p->ret = &&point1;

begin | p->dyn = bac; bac = p;

x := f(3); | goto f;

writeln(x); |point1:

end. | p = bac->dyn; free(bac); bac = p;

| x = f_res;

| printf("%d",x);

| return;

|

|f: if (bac->n <= 1)

| f_res = 1;

| else

| { p = malloc(sizeof(bloc));

| p->n = bac->n-1; p->ret = &&point2;

| p->dyn = bac; bac = p;

| goto f;

| point2:

+---+ | p = bac->dyn; free(bac); bac = p;

bac-->| 3 | n | f_res = bac->n * f_res;

|&&point1| ret | }

| NULL | dyn | goto *bac->ret;

+---+ |}

(57)

c 2006 Marc Feeley IFT2030 page 546

Activation: cas récursif (5)

program recursif; |typedef struct bloc

| { int n; void *ret; struct bloc *dyn; } bloc;

var x : integer; |

|bloc *bac, *p;

function f(n:integer)|int x;

: integer; |int f_res;

begin |

if n <= 1 then |void recursif()

f := 1 |{

else | bac = NULL;

f := n * f(n-1); |

end; | p = malloc(sizeof(bloc));

| p->n = 3; p->ret = &&point1;

begin | p->dyn = bac; bac = p;

x := f(3); | goto f;

writeln(x); |point1:

end. | p = bac->dyn; free(bac); bac = p;

| x = f_res;

| printf("%d",x);

| return;

|

|f: if (bac->n <= 1)

| f_res = 1;

+---+ | else

bac-->| 2 | n | { p = malloc(sizeof(bloc));

|&&point2| ret | p->n = bac->n-1; p->ret = &&point2;

| . | dyn | p->dyn = bac; bac = p;

+---|----+ | goto f;

V | point2:

+---+ | p = bac->dyn; free(bac); bac = p;

| 3 | n | f_res = bac->n * f_res;

|&&point1| ret | }

| NULL | dyn | goto *bac->ret;

+---+ |}

(58)

c 2006 Marc Feeley IFT2030 page 547

Activation: cas récursif (6)

program recursif; |typedef struct bloc

| { int n; void *ret; struct bloc *dyn; } bloc;

var x : integer; |

|bloc *bac, *p;

function f(n:integer)|int x;

: integer; |int f_res;

begin |

if n <= 1 then |void recursif()

f := 1 |{

else | bac = NULL;

f := n * f(n-1); |

end; | p = malloc(sizeof(bloc));

| p->n = 3; p->ret = &&point1;

begin | p->dyn = bac; bac = p;

x := f(3); | goto f;

writeln(x); |point1:

end. | p = bac->dyn; free(bac); bac = p;

+---+ | x = f_res;

bac-->| 1 | n | printf("%d",x);

|&&point2| ret | return;

| . | dyn |

+---|----+ |f: if (bac->n <= 1)

V | f_res = 1;

+---+ | else

| 2 | n | { p = malloc(sizeof(bloc));

|&&point2| ret | p->n = bac->n-1; p->ret = &&point2;

| . | dyn | p->dyn = bac; bac = p;

+---|----+ | goto f;

V | point2:

+---+ | p = bac->dyn; free(bac); bac = p;

| 3 | n | f_res = bac->n * f_res;

|&&point1| ret | }

| NULL | dyn | goto *bac->ret;

+---+ |}

(59)

c 2006 Marc Feeley IFT2030 page 548

Activation: gestion mémoire (1)

Quand faut-il récupérer l’espace occupé par le bloc d’activation?

La plupart des langages le font au retour de

l’activation (exceptions: Modula-3, Oberon, Lisp, Scheme)

Un problème relié à ce choix, en C, c’est que

l’opérateur “&” peut engendrer des pointeurs fous

int *p;

void print () { printf ("%d\n", *p); } void proc (int n) { p = &n; print (); } void main () { proc (123); print (); }

(60)

c 2006 Marc Feeley IFT2030 page 549

Activation: gestion mémoire (2)

Dans les langages comme C et Pascal, qui spécifient que le bloc d’activation est récupéré au retour de

l’activation, il est possible d’allouer les blocs d’activation sur une pile car le bloc d’activation de l’appelée est

seulement actif pendant l’activation de l’appelant

Dans ce cas, le lien dynamique n’a plus besoin d’être stocké explicitement car les blocs sont contigus

(61)

c 2006 Marc Feeley IFT2030 page 550

Activation: gestion mémoire (3)

program recursif; | typedef struct bloc

| { int n;

var x : integer; | void *ret;

| } bloc;

function f(n:integer) |

: integer; | bloc pile[100], *bac;

begin | int x;

if n <= 1 then | int f_res;

f := 1 |

else | void recursif()

f := n * f(n-1); | { bac = pile;

end; |

| (bac+1)->n = 3;

begin | (bac+1)->ret = &&point1;

x := f(3); | bac++;

writeln(x); | goto f;

end. | point1:

| bac--;

| x = f_res;

+---+ | printf("%d",x);

bac-->| 1 | n | return;

|&&point2| ret |

+---+ | f: if (bac->n <= 1)

| 2 | n | f_res = 1;

|&&point2| ret | else

+---+ | { (bac+1)->n = bac->n-1;

| 3 | n | (bac+1)->ret = &&point2;

|&&point1| ret | bac++; goto f;

+---+ | point2:

| | n | bac--;

| | ret | f_res = bac->n * f_res;

+---+ | }

| goto *bac->ret;

| }

(62)

c 2006 Marc Feeley IFT2030 page 551

Activation: gestion mémoire (4)

L’allocation et la récupération des blocs d’activation peuvent se faire d’un seul coup (tel que fait

précédemment) ou incrémentallement

L’allocation et récupération incrémentale se fait en partie dans l’appelant et en partie dans l’appelé

(63)

c 2006 Marc Feeley IFT2030 page 552

Activation: gestion mémoire (5)

Par exemple, les compilateurs C font normalement

• L’allocation d’un bloc contenant les paramètres dans l’appelant (empilés dans l’ordre droite à gauche)

• L’extension de ce bloc avec les variables locales dans l’appelé

• La contraction de ce bloc en quittant la portée des variables locales dans l’appelé

• La récupération du bloc au retour de l’activation dans l’appelant

(64)

c 2006 Marc Feeley IFT2030 page 553

Activation: gestion mémoire (6)

Exemple en C

void proc (int x, int y) { int a;

a = x-y;

if (a < 0)

{ int b; b = a*a; printf ("%d", b); } printf ("%d %d %d", x, y, a); /* 1 2 -1 */

}

void test () { proc (1, 2); }

y x

y x a

y x a b

y x a b

y x a b

y x a

y x a

y x a

y x

ret

ret ret ret ret ret ret ret ret ret ret ret

ret ret ret ret ret ret ret ret

fmt

ret

ret

2 1

2 1 -1

2 1 -1

1

2 1 -1

1 1

"%d"

2 1 -1

1

2 1 -1

2 1 -1

2 1 -1

2 1

fmt "%d %d %d"

-1 2 1

test proc proc proc printf proc proc printf proc test

(65)

c 2006 Marc Feeley IFT2030 page 554

Activation: gestion mémoire (7)

Empiler les paramètres de droite à gauche permet une implantation simple des fonctions à arité variable

comme printf:

int printf (char* fmt, ...) /* v´eritable "..." */

{

/* utiliser "fmt" pour obtenir les autres param`etres */

}

(66)

c 2006 Marc Feeley IFT2030 page 555

Activation: procédures imbriquées (1)

Une approche répandue pour gérer la mémoire est de la subdiviser en 3 zones

1. zone statique: variables globales et code 2. pile: paramètres et variables locales

3. tas: autres allocations dynamiques

00000 00000 00000 00000 11111 11111 11111 11111 0000000

0000000 0000000 0000000 1111111 1111111 1111111 1111111

00000 00000 00000 00000 11111 11111 11111 11111

zone statique tas pile

L’accès à une variable globale est simple car l’adresse de la variable dans la zone statique est connue à la

compilation

(67)

c 2006 Marc Feeley IFT2030 page 556

Activation: procédures imbriquées (2)

L’accès à une variable non-globale demande de

trouver le bloc d’activation la contenant et d’accéder au champ approprié de ce bloc

• En C et FORTRAN ceci est relativement simple

puisque la variable est nécessairement dans le bloc d’activation courant

• Avec des procédures imbriquées ceci n’est plus vrai

(68)

c 2006 Marc Feeley IFT2030 page 557

Activation: procédures imbriquées (3)

Exemple en Pascal avec lien dynamique

program imbrique;

var x : integer;

procedure aa (a : integer);

procedure bb (b : integer);

begin writeln (b + a); end;

procedure cc (c : integer);

begin bb (c*10); end;

begin bb (10); cc (20); end;

begin aa (1); (* 11 201 *) aa (2); (* 12 202 *) end.

dyn ret

a 1

NULL

aa_1

dyn ret

b 10

dyn ret

a 1

NULL

bb_1

aa_1

dyn ret

a 1

NULL

aa_1

dyn ret

c 20

dyn ret

a 1

NULL

aa_1 cc_1

dyn ret

b 200

dyn ret

a 1

NULL

dyn ret

c 20

cc_1 bb_2

aa_1

(69)

c 2006 Marc Feeley IFT2030 page 558

Activation: procédures imbriquées (4)

Déf: le parent dynamique d’une activation X c’est l’activation Y qui a créée l’activation X

imbrique_1 aa_1

cc_1 bb_1

bb_2

aa_2 bb_3 cc_2 bb_4

Déf: le parent lexical d’une activation X c’est

l’activation Y qui a déclarée la procédure dont l’appel a créé l’activation X

imbrique_1 aa_1

cc_1 bb_1

aa_2 bb_3 cc_2

bb_2 bb_4

Le lien dynamique chaîne les parents dynamiques et le lien statique chaîne les parents lexicaux

(70)

c 2006 Marc Feeley IFT2030 page 559

Activation: procédures imbriquées (5)

La chaîne statique permet de trouver le bloc d’activation contenant une variable à accéder

Déf: le niveau d’imbrication d’une procédure c’est sa profondeur dans l’arbre d’imbrication (avec racine = 1)

bb aa imbrique

cc

niveau 1 niveau 2 niveau 3

(71)

c 2006 Marc Feeley IFT2030 page 560

Activation: procédures imbriquées (6)

Si l’exécution se trouve dans la procédure X de niveau x

et qu’il faut accéder à une variable V déclarée dans la procédure englobante Y de niveau y alors

• le bloc contenant V est à une distance de x − y dans la chaîne statique par rapport au bloc d’activation

courant

imbrique_1 aa_1

cc_1 bb_1

aa_2 bb_3 cc_2

bb_2 bb_4

Références

Documents relatifs

6) Le calcul de l’amplitude A fournit une valeur qui tend vers l’infini lorsque la fr´ equence tend vers une fr´ equence propre. Or on observe des amplitudes importantes mais

sur la fonction

La torsion de la ligne de striction est constante en même temps que le paramètre

&#34;Riemann ordinaire pour laquelle x\ reste inférieur à un nombre fixe r. Ces cercles, les lignes de ramification qui. Coursât {Bulletin de la Société rnathématique de Fmnce^

Ainsi le stabili- sateur d'une loi de groupe de hauteur finie à coefficients dans un corps K de caractéristique p algébriquement clos est invariant pour toute extension de K.

ne contiendra plus t dans sa portion de degré zéro. Ainsi : Pour qu'un système à fonction V possède ^intégrale des forces vives^ il faut et il suffit qu 7 il possède une

Montrer que F est de classe C ∞ sur R et vérie une équation diérentielle linéaire du second ordre..

Dans ce cas on « transforme » une des deux variables à l’aide d’une fonction pour obtenir un nouveau nuage qui aura la forme d’une droite. Exemple 12.5 (D’après