Proc edures, port ee et activations
Transcript of Proc edures, port ee et activations
Procedures, portee et activations
IFT 2030, v 1.0
Universite de Montreal Portee et activations 1
Variables
def : variable = abstraction d’emplacement en memoire
(von Neumann !)
donc : variable =
nom [identificateur] (sauf pour variables anonymes allouees
dynamiquement)
+ adresse [l-value] (sur la pile ou le tas)
+ valeur [r-value]
+ type (ensemble de valeurs possibles)
+ duree de vie (pointeurs !)
+ portee [scope] : visibilite du nom
IFT 2030, v 1.0
Universite de Montreal Portee et activations 2
Variables II
duree de vie : temps pendant lequel la variable est liee a une
addresse en memoire
I. statique : meme adresse pendant l’execution du programme
(toutes variables en FORTRAN, static en C, C++ et Java)
II. dynamique sur la pile (variables locales dans procedures)
III. dynamique sur le tas, allouee explicitement
(acces par pointeurs)
IV. dynamique sur le tas, allouee implicitement
(p.e., chaınes de caracteres en Perl et JavaScript)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 3
Portee
Les noms sont utilises pour identifier des types, variables, procedures,
etc. (identificateurs)
Dans la plupart des langages un meme nom peut servir a designer
plusieurs choses
Les regles de portee d’un langage specifient ce qui est designe
par un nom, en fonction du contexte de son utilisation
Def : une declaration introduit un nouveau sens pour un nom
declaration de type, de variable, de parametre, de procedure, . . .
Exemple : int x; ⇒ declaration qui introduit le nom x
IFT 2030, v 1.0
Universite de Montreal Portee et activations 4
Declaration
Declaration de variables peut etre implicite : p.e. FORTRAN
Typiquement : la declaration fixe le type d’une variable et la
portee de son nom.
Souvent : la declaration alloue de la memoire (p.e. Java) aussi
Exemple en Algol 68 :
la declaration int x ; equivaut a ref int x=loc int ;
(loc pour allocation de memoire sur la pile)
philosophie : x est «un pointeur constant» vers une valeur entiere
(par contre int y=9 ; signifie que y est un entier constant)
C et C++ : definition = declaration avec allocation
IFT 2030, v 1.0
Universite de Montreal Portee et activations 5
Portee II
Def : la portee d’une declaration = la region du programme ou
l’utilisation du nom declare designe cette declaration.
Exemple en C
int carre (int x) { return x*x; }
int f (int y){ return carre (carre (y)); }
– Portee de int x = corps de carre
– Portee de int carre (int x) = corps de carre et f
IFT 2030, v 1.0
Universite de Montreal Portee et activations 6
Portee III
Les regles de portee forment une fonction (utilisee par le com-
pilateur ou interprete)
nom 7→ declaration
Les deux sortes de portee les plus repandues :
1. Portee lexicale/statique (plupart des langages) : si fonction
calculable a la compilation
2. Portee dynamique (Perl, APL, TeX, Lisp mais pas Scheme) :
si fonction calculable a l’execution
IFT 2030, v 1.0
Universite de Montreal Portee et activations 7
Portee lexicale
Regle : un nom designe la declaration englobante la plus proche
[vers le debut] textuellement
Donc, une declaration locale a un bloc (ou procedure) a preceance,
a l’interieur de ce bloc, sur toute declaration du meme nom ex-
terne a ce bloc
Note : en C, une «declaration» de variable n’inclut pas l’initia-
lisation (i.e. la portee de int x debute apres l’initialisation dans
int x = x*y;)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 8
Portee lexicale II
Exemple en C++
IFT 2030, v 1.0
Universite de Montreal Portee et activations 9
Portee lexicale III
Pascal : declarations au debut de procedures ⇒ nouvelle portee
introduisable par une procedure seulement
Certains langages permettent l’imbrication des procedures (Pas-
cal, Modula-2, Ada, SIMULA, Scheme, Perl mais pas C et FOR-
TRAN)
La portee des declarations locales a une procedure s’etend aux
sous-procedures
IFT 2030, v 1.0
Universite de Montreal Portee et activations 10
Portee lexicale IV
Exemple en Pascal
procedure A;
var x : char; (* variable locale de A *)
procedure B;begin
writeln(x)
end;
procedure C;
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;
IFT 2030, v 1.0
Universite de Montreal Portee et activations 11
Portee dynamique
Regle : un nom designe la declaration englobante la plus recente
dans la chaıne d’appel
Donc, si la procedure A appele la procedure B, une declaration
qui est visible dans A le sera egalement dans B (a moins que B
declare le meme nom)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 12
Portee dynamique II
Exemple en Perl
sub A{ local $x;sub B { printf("%s\n", $x);}sub C
{ local $x = "C"; B;}
$x = "A";B; # imprime AC; # imprime CB; # imprime A
}
Note : en Perl, l’utilisation de my a la place de local donne la
portee lexicale
IFT 2030, v 1.0
Universite de Montreal Portee et activations 13
Portee dynamique III
En general la portee dynamique est a eviter car sa semantique
depend de l’ordre d’execution
Un cas ou la portee dynamique est appropriee, c’est pour passer
un parametre a toutes les procedures appelees pendant l’activa-
tion courante
La portee lexicale se compile mieux (programmes plus rapides)
que la portee dynamique car la correspondance nom-declaration
est connue a la compilation
IFT 2030, v 1.0
Universite de Montreal Portee et activations 14
Portee dynamique IV
Exemple en C : changer la destination d’impression
void imprime_ligne (char* ligne, FILE* F){
while (*msg != ’\0’) fputc (*msg++, F);
fputc (’\n’, F);}
void imprime_page (char** page, int nombre_lignes, FILE* F){
int i;
for (i=0; i<nombre_lignes; i++) imprime_ligne (page[i], F);
}
char* p[3] = { "ligne 1", "ligne 2", "ligne 3" };
void main (void){
FILE* fich;
imprime_page (p, 3, stdout);fich = fopen ("rapport", "w");
imprime_page (p, 3, fich);
fclose (fich);
}
IFT 2030, v 1.0
Universite de Montreal Portee et activations 15
Procedures
def : procedure = fragment de programme parametrise
def : signature d’une procedure = type des parametres (et du
resultat dans le cas d’une fonction)
def : corps d’une procedure = operations associees a la procedure
Une definition de procedure donne generalement
1. Le nom de la procedure
2. Sa signature
3. Le nom des parametres formels
4. Le corps
IFT 2030, v 1.0
Universite de Montreal Portee et activations 16
Procedures II
def : appel de procedure = utilisation de la procedure dans le
texte du programme
Def : activation de procedure = execution du corps de la procedure
Exemple en C :
double demi(int n){ /* signature: int -> double */return n*0.5;
}void test (void){int i;double x = 0;for (i=1; i<=5; i++) x += demi(i);
}
1 appel et 5 activations de demi
IFT 2030, v 1.0
Universite de Montreal Portee et activations 17
Procedures III
2 procedures intervenantes par activation :
1. La procedure appelante contient l’appel
2. La procedure appelee est celle qui est activee par l’appel
Un appel de procedure contient les parametres actuels
Les parametres formels sont les representants des parametres
actuels dans le corps de l’appelee
IFT 2030, v 1.0
Universite de Montreal Portee et activations 18
Benefices
1. Abstraction : permet d’exprimer des operations abstraites,
i.e. plus proches des besoins de l’application (trier, chercher,
etc.)
2. Isoler l’implantation : si la specification de la procedure est
bien definie, le corps de la procedure peut etre change/ameliore
sans avoir a changer le reste du programme
3. Modularite : un programme peut etre decoupe en morceaux
qui sont plus faciles a comprendre independamment
4. Librairies : groupe de procedures d’usage general reutilisable
par divers programmes (stdio, Xlib, . . . )
IFT 2030, v 1.0
Universite de Montreal Portee et activations 19
Variantes
1. Syntaxe de definition
– avec mot de cle (FORTRAN : SUBROUTINE, Pascal : procedure)
– ou sans (C, C++, Java : void)
2. Retour du resultat de la fonction :
– avec mot de cle (C,. . . : return) — possiblement plusieurs fois
dans la meme fonction– nom de fonction a la gauche (Pascal)
fonction square(x : entier) : entier;debut
square := x ∗ xfin ;
– rien (derniere expression evaluee dans la fonction) (Algol)
3. Appel :
en general : 〈nom〉(〈parametres〉)
(Pascal : parentheses facultatives si aucun parametre)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 20
Parametres optionnels
Parametres optionnels (C++, Ada)
– valeur de defaut : si parametre n’est pas fourni, la valeur de
defaut est utilisee– exemple en C++
void print_int(int n, int base=10, FILE f*=stdout){...
}
void proc(){print_int(13); // decimal sur stdout
print_int(13,2); // binaire sur stdout
print_int(555,16,stderr); // hexadecimal sur stderr}
IFT 2030, v 1.0
Universite de Montreal Portee et activations 21
Parametres nommes
– Ada, Lisp et Perl permettent de specifier les parametres actuels
en les nommant (dans n’importe quel ordre)
– utile lorsque combine avec parametres optionnels 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 isbegin
header (100, "", False);header (100, center => False);header (center => False, page => 100);
end example;
IFT 2030, v 1.0
Universite de Montreal Portee et activations 22
Passage de parametre
def : mode de passage de parametre = lien qui existe entre le
parametre formel et le parametre actuel
Les 4 modes les plus repandus
1. par valeur (existe dans presque tous les langages ; aussi ap-
pele par copie)
2. par reference (Pascal, Modula-2, C++, Java pour les objets)
3. par valeur-resultat (Ada)
4. par nom (Algol, SIMULA)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 23
Passage par valeur
Parametre actuel est une expression (R-value)
Parametre formel est une variable creee pour l’activation et ini-
tialisee avec la valeur du parametre actuel
/* exemple de passage par valeur en C*/int carre (int x){ x = x*x; return x; }
void test (void){ int n = 3;printf ("%d", carre (n)); /* 9 */printf ("%d", n); /* 3 */printf ("%d", carre (n+1)); /* 16 */
}
Donc, les affectations au parametre formel n’affectent pas le
parametre actuel
IFT 2030, v 1.0
Universite de Montreal Portee et activations 24
Passage par reference
Parametre actuel est une variable ou tout autre emplacement
memoire (L-value)
Parametre formel a la meme addresse que celle du parametre
actuel lors de l’activation (i.e. c’est un alias)
/* exemple de passage par reference en C++ */int carre (int &x) /* x passe par reference */{ x = x*x; return x; }
void test (void){ int n = 3;printf ("%d", carre (n)); /* 9 */printf ("%d", n); /* 9 */
}
(Le meme effet peut etre obtenu en C avec le passage par valeur
d’un pointeur)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 25
Passage par valeur-resultat
Parametre actuel est une variable ou tout autre emplacement
memoire (L-value)
Parametre formel est une variable creee pour l’activation et ini-
tialisee avec la valeur du parametre actuel
Au retour de la procedure, le parametre formel est copie dans le
parametre actuel
IFT 2030, v 1.0
Universite de Montreal Portee et activations 26
Passage par valeur-resultat II-- exemple de passage par valeur-resultat en Adai, n : integer;
procedure add2 (x : in out integer) isbeginx := x+i;x := x+i;
end;
n := 0; i := 1;add2 (n); put (n); -- imprime 2 (serait 2 par reference)add2 (i); put (i); -- imprime 3 (serait 4 par reference)
Permet acces plus rapide au parametre formel que passage par
reference (evite indirection)
Sens plus simple pour les systemes concurrents
IFT 2030, v 1.0
Universite de Montreal Portee et activations 27
Passage par nom
Parametre actuel est un L-value ou un R-value
Une utilisation du parametre formel evalue le parametre actuel
comme L-value (si a gauche d’une affectation) sinon comme
R-value
Le parametre actuel est evalue a chaque acces
IFT 2030, v 1.0
Universite de Montreal Portee et activations 28
Passage par nom II
Exemple en SIMULA
integer i, n;integer array t(1:2);
procedure add2 (x); name x; integer x;beginx := x+1;i := i+1;x := x+1;
end;
n := 0; i := 1;add2 (n);outint (n,0); ! imprime 2 (serait 2 par reference) ;
n := 0; i := 1; t(1) := 0; t(2) := 0;add2 (t(i));outint (t(1),0); ! imprime 1 (serait 2 par reference) ;outint (t(2),0); ! imprime 1 (serait 0 par reference) ;
IFT 2030, v 1.0
Universite de Montreal Portee et activations 29
Passage par nom IIILes subtilites de la semantique du passage par nom peuvent etre exploiteespour creer des nouvelles structures de controle
Exemple : calcul d’une somme generale, tel que∑10
i=1 2−i.
real procedure somme (index, bas, haut, valeur);name index, valeur;integer index, bas, haut;real valeur;
begininteger j;real s;s := 0;for j := bas step 1 until haut do
beginindex := j;s := s + valeur;
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 ;
IFT 2030, v 1.0
Universite de Montreal Portee et activations 30
Macros
Le compilateur C (gcc) est compose de plusieurs programmes
qui traitent a leur tour le programme a compiler
cpp est le “preprocesseur C”
IFT 2030, v 1.0
Universite de Montreal Portee et activations 31
Macros II
cpp traite toutes les directives de preprocesseur # et les macros
– #include : inclusion de fichier
– #if ou #ifdef, #else, #endif : compilation conditionnelle
– #define : definition de macro
def : une macro c’est une sorte de procedure dont l’appel sera
traite par le preprocesseur, en remplacant l’appel par le corps de
la macro avec une substitution textuelle des parametres
IFT 2030, v 1.0
Universite de Montreal Portee et activations 32
Macros III#include <stdio.h> | int printf (char *format, ...);
| ... /* 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;
} | }main () | main (){ | {#ifdef sun |
printf ("version SUN\n"); |#endif |#ifdef SGI |
printf ("version SGI\n"); | printf ("version SGI\n");#endif |} | }
IFT 2030, v 1.0
Universite de Montreal Portee et activations 33
Macros IV
Les macros sont souvent utilisees au lieu de procedures pour
eviter le cout d’appel (copie des parametres, transferts de controle,. . . )
Le traitement des macros par expansion textuelle peut cependant
accroıtre la taille de l’executable
Si la macro contient des declarations : une sorte de portee dy-
namique — resultats inattendus
#define echange(x,y) { int t = y; y = x; x = t; }
void proc (int a, int b, int t, int t2){ echange(a,b); /* { int t = b; b = a; a = t; }; */echange(t,t2); /* { int t = t2; t2 = t; t = t; }; */
/* le deuxieme appel ne change pas t et t2 */}
IFT 2030, v 1.0
Universite de Montreal Portee et activations 34
Macros V
Les macros ont d’autres differences semantiques avec les procedures
1. Categorie syntaxique de l’expansion possiblement differente
de l’appel (idem pour le parametre formel)
#define abs(x) (x<0) ? -x : x
#define echange(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) */echange(a,b); /* { int t = b; b = a; a = t; }; */
else ... /* else ... <-- erreur de syntaxe */
une solution :
#define abs(x) (((x)<0) ? -(x) : (x))
#define echange(x,y) do { int t = y; y = x; x = t; } while (0)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 35
Macros VI
2. Evaluation multiple du parametre actuel (ceci est un probleme
difficile a contourner en meme temps que les conflits de noms)
b = abs(f(a));/* b = (((f(a)+1)<0) ? -(f(a)+1) : (f(a)+1))*2; */
echange(x[i++],x[j++]);/* do {int t=x[j++];x[j++]=x[i++];x[i++]=t;} while (0); */
une solution :
#define echange(x,y) \do { int *px=&(x), *py=&(y), t=*py; *py=*px; *px=t; } while (0)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 36
Activation
Objectif : expliquer les mecanismes d’activation de procedure et
d’acces aux variables
Probleme fondamental : comment associer une valeur a une va-
riable ?
nom de variable → valeur de la variable
Decompose en 3 etapes :
1. nom → declaration : portee
2. declaration → emplacement memoire : activation courante
3. emplacement memoire → valeur : etat de la memoire
IFT 2030, v 1.0
Universite de Montreal Portee et activations 37
Activation II
La deuxieme etape est necessaire pour traiter les procedures
recursives
def : une procedure est recursive s’il est possible que pendant
son activation elle soit activee a nouveau
directement recursive : f appelle f
indirectement recursive : f appelle g qui appelle f
IFT 2030, v 1.0
Universite de Montreal Portee et activations 38
Activation IIIunsigned int f(unsigned int n){unsigned int retval;if (n==0) retval=1;else retval = n*f(n-1);return retval;
}
a l’execution d’un appel f(2), il y a 3 activations de f :
chaque activation a sa propre copie de la variable n
le nombre d’appels recursifs n’est pas connu statiquement :
recursivite ⇒ allocation dynamique des variables locales et pa-
rametres formels — pile !
IFT 2030, v 1.0
Universite de Montreal Portee et activations 39
Activation : cas non-recursif
L’espace pour stocker les parametres et variables locales des
procedures non-recursives peut etre alloue statiquement (e.g.
FORTRAN)
La procedure appelante doit indiquer a la procedure appelee ou
il faut retourner le controle apres l’activation (adresse de retour)
L’adresse de retour est simplement un parametre implicite de
toutes les procedures
IFT 2030, v 1.0
Universite de Montreal Portee et activations 40
Activation : cas non-recursif II
Exemple : FORTRAN 77
variables globales
bloc d'activation: MAIN
bloc d'activation: R
variables locales
paramètres
adresse de retourcode: MAIN
code: R
SUBROUTINE R(I)
INTEGER I
REAL F9, H2
COMMON F9
...
RETURN
END
PROGRAM MAIN
INTEGER J
REAL G
COMMON G
...
CALL R(J+9)
...
STOP
END
organisation de mémoire source
IFT 2030, v 1.0
Universite de Montreal Portee et activations 41
Activation : cas recursif
L’etat d’une activation est conserve dans un bloc d’activation
qui stocke
– Les parametres formels (incluant l’adresse de retour)
– Les variables locales et temporaires (introduites par le compi-
lateur)
– Le resultat de la fonction (pas vraiment necessaire car alloca-
tion statique possible)
– Le lien dynamique qui chaıne les blocs des activations qui n’ont
pas encore terminees
Le lien dynamique permet de retrouver le bloc de la procedure
appelante au moment du retour de la procedure courante
IFT 2030, v 1.0
Universite de Montreal Portee et activations 42
Activation : gestion memoire
L’allocation et la recuperation du bloc d’activation peut se faire
d’un seul coup ou incrementallement
L’allocation et recuperation incrementale se fait en partie dans
l’appelant et en partie dans l’appele
Par exemple, les compilateurs C font normalement
– L’allocation d’un bloc contenant les parametres dans l’appe-
lant (empiles dans l’ordre droite a gauche)
– L’extension de ce bloc avec les variables locales dans l’appele
– La contraction de ce bloc en quittant la portee des variables
locales dans l’appele
– La recuperation du bloc au retour de l’activation dans l’appe-
lantIFT 2030, v 1.0
Universite de Montreal Portee et activations 43
Activation : gestion memoire II
Exemple en C :
variables statiques
bloc d'activation: main
bloc d'activation: f(1)
locales et temps
adresse de retour
paramètres
code: main
code: f
const int N=100;
int f(unsigned n){
int v;
if (n==0) v=1;
else v = n*f(n-1);
return v;
}
void main(void){
int k;
...
k=f(1);
...
}
bloc d'activation: f(0)
PILE
point de référence (frame pointer)
fp+0
fp-8
STATIQUES
(il n’est pas necessaire de stocker le lien dynamique)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 44
Recursion terminale
def : un appel terminal c’est un appel de fonction qui est en
position terminale
def : une fonction recursive est en forme iterative si les appels
recursifs sont tous des appels terminaux
unsigned int fact_aux(unsigned int n, unsigned int v){if (n==0) return v;else return fact_aux(n-1, n*v);
}
unsigned int fact(unsigned int n){return fact_aux(n, 1);
}
⇒ reutiliser le bloc d’activation courant pour la nouvelle activa-
tion : espace constant au long de la recursion
IFT 2030, v 1.0
Universite de Montreal Portee et activations 45
Activation : proceduresimbriquees
acces aux variables avec portee lexicale :
– L’acces a une variable globale est simple car l’adresse de la
variable dans la zone statique est connue a la compilation
– L’acces a une variable non-globale demande de trouver le bloc
d’activation la contenant et d’acceder au champ approprie de
ce bloc
– En C : non-globale est dans le bloc d’activation courant
– Procedures imbriquees : bloc d’activation peut etre tres loin
dans la chaıne d’appel — lien statique vers parent lexical
IFT 2030, v 1.0
Universite de Montreal Portee et activations 46
Procedures imbriquees II
Exemple en Pascal
variables globales
BA: imbrique
BA: aa(1)
code
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.
BA: cc(20)
BA: bb(200)
lien dynamique
lien statique
locales et temps
(résultat)PILE
paramètres
IFT 2030, v 1.0
Universite de Montreal Portee et activations 47
Procedures imbriquees III
Niveau d’imbrication = nombre de parents lexicaux
acces a une variable non-globale v :
– nombre de liens statiques a traverser
– deplacement dans le bloc contenant v (calculable a la compi-
lation)
marche parce que :
1. parent statique est toujours dans la chaıne statique
2. niveaux d’imbrication calculables a la compilation
IFT 2030, v 1.0
Universite de Montreal Portee et activations 48
Procedures imbriquees IV
procedure passee en parametre (C, Pascal, Simula, Scheme, AL-
GOL, . . . )
procedure fn_en_param;
function derive(function f(w: real):real, x:real):real;
begin derive := (f(x+0.001)-f(x-0.001))/0.002 end;
procedure test(exp: integer);
function puiss(z:real):real; (* calcule z^exp *)
var temp:real ; i:integer;
begin
temp:=1; for i:=1 to exp do temp := temp * z;puiss := temp
end;
begin writeln(derive(puiss,10.0)) end;
begin
test(3) (* 300.000001 *)
end;
IFT 2030, v 1.0
Universite de Montreal Portee et activations 49
Procedures imbriquees V
procedure passee en parametre : problemes
1. niveau d’imbrication n’est pas constant
2. parent statique n’est pas necessairement dans la chaıne sta-
tique
un parametre procedural est represente par un enregistrement
contenant
1. pointeur vers le code executable
2. pointeur vers le parent statique
IFT 2030, v 1.0
Universite de Montreal Portee et activations 50
Displays
def : un «display» c’est un tableau de pointeurs vers des blocs
d’activation
plutot que representer la chaıne statique comme une liste de
blocs, utiliser le display :
display[niveau]= pointeur vers bloc dans la chaıne a ce niveau
d’imbrication
acces se fait en temps constant ; taille maximale calculable a la
compilation (max. niveau d’imbrication)
lors d’un appel a f il faut sauvegarder l’entree du display au
niveau de f
(ou plusieurs entrees si une procedure est passee en parametre)
IFT 2030, v 1.0
Universite de Montreal Portee et activations 51