common
external
Il arrive fréquemment que l'on doive faire plusieurs fois la même chose au sein d'un même programme, mais dans un contexte différent. Par exemple :
On a un problème décomposable en plusieurs sous problèmes. On cherche à « enfermer » chaque sous problème dans un bloc et à faire communiquer ces blocs.
Exemple : écrire un programme qui lit trois matrices au clavier, en fait le produit, puis affiche le résultat. On écrira trois blocs de base :
Les objets FORTRAN correspondants à ces blocs sont les subroutines ou les fonctions.
On voit que chacun de ces blocs peut être écrit séparément, et qu'il est relié à l'extérieur par des « portes » d'entrée/sortie repérées par un nom. Persuadons-nous que ce nom n'a de sens que pour le bloc, et qu'il est là pour identifier une entrée ou une sortie.
Les connexions des boites avec l'extérieur sont appelés en FORTRAN « paramètres formels », et seront traités comme des variables dans les instructions exécutables.
Avant d'aller plus loin, montrons comment utiliser les trois blocs définis ci-dessus pour résoudre le problème proposé.
Symboliquement cela revient à connecter les blocs comme le montre la figure 10.1.
C'est une séquence d'instructions appelable d'un point quelconque du programme. Elle peut être appelée depuis le programme principal, ou depuis une autre subroutine ou fonction.
Une subroutine est définie par :
|
Les instructions de la subroutine peuvent manipuler :
Les instructions de la subroutine ne peuvent pas manipuler les variables locales du programme principal ou d'une autre subroutine ou fonction.
L'appel d'une subroutine se fait depuis un bloc fonctionnel quelconque
(programme principal, subroutine, fonction) avec l'instruction call
.
|
Les arguments v1
, v2
, v3
peuvent être :
Les types des arguments v1 , v2 ,
v3 ,... doivent correspondre exactement à ceux des
paramètres formels pf1 , pf2 ,
pf3 ,...
|
Écrire une subroutine qui calcule les coordonnées polaires associées à des coordonnées cartésiennes (x,y).
|
Le figure 10.2 donne un code source possible de cette subroutine.
Un exemple d'appel de cette subroutine dans le programme principal :
programm
|
a
, b
, rho
et phi
sont des variables locales du programme principal : elles n'ont aucun sens
pour la subroutine polar
.
pi
et temp
sont des variables locales
de la subroutine polar
: elles n'ont aucun sens pour le programme principal.
x
, y
, r
et theta
sont les
paramètres formels de la subroutine polar
. Ce sont les portes de
communication de la subroutine et leurs noms n'ont aucun sens à l'extérieur
de la subroutine.
En particulier, s'il y a dans le programme principal des variables locales
appelées x
, y
, r
ou theta
, elles
n'ont rien à voir avec les paramètres formels de la subroutine.
En revanche, il est possible de passer ces variables locales en tant
qu'arguments d'appel à polar
:
call polar (x, y, rho, phi) |
La variable locale x
du programme principal est alors passée à la
subroutine via son premier paramètre formel, qui incidemment s'appelle
aussi x
, mais les deux objets sont bien distincts.
Réécrire le programme de résolution des équations du second degré avec des subroutines.
Dans le programme de la figure 10.3, il y a trois erreurs ô combien classiques. Corrigez-les, puis répondez ensuite aux questions suivantes :
integer
pour coder n! ?
Complétez ensuite les subroutines pour qu'elles calculent effectivement n! et Cnp.
|
Une fonction est en tout point identique à une subroutine, mais son nom
contient en plus une valeur. C'est-à-dire qu'au lieu de l'appeler par
call
, on l'utilise à droite du signe =
dans une
instruction d'affectation.
On avait déjà vu les fonctions prédéfinies du FORTRAN, par exemple
datan
, qui n'a qu'un paramètre formel :
pi=4*datan(1d0) |
Puisque le nom de la fonction contient une valeur, cette valeur doit être
typée. Par exemple datan
renvoie une valeur double precision
. En
plus du type de ses paramètres formels, la définition d'une fonction doit
donc décrire le type de la valeur qu'elle retourne.
La valeur de retour de la fonction sera affectée dans le corps de la fonction, comme si le nom de la fonction était une variable ordinaire.
|
Le type peut être omis auquel cas le type de la fonction est fixé par les règles par défaut.
Une fonction est utilisable dans tout autre bloc fonctionnel, comme une variable qui aurait des arguments. Comme pour les subroutines, il est indispensable de respecter la correspondance entre les arguments passés à la fonction et ses paramètres formels.
Attention : il faut non seulement déclarer le type de la fonction lors de sa définition, mais aussi dans tous les blocs fonctionnels où on l'utilise. Si cette déclaration est absente, les règles de typage automatiques du bloc fonctionnel courant s'appliquent.
Fonction qui calcule le rayon-vecteur associé à un couple (x,y) : figure 10.4.
|
Fonction qui saisit un caractère au clavier : figure 10.5.
|
Les variables locales des divers blocs fonctionnels sont stockées à une adresse mémoire fixée par le compilateur. Cette adresse mémoire est un grand entier, qui est « le numéro de bureau » de la variable.
Certains bureaux sont plus grands que d'autres. Une variable integer
ou real
occupera un bureau à 4 cases, une variable double precision
un bureau à 8 cases, un vecteur de 100 real
, un bureau de
cases, une matrice de
double precision
un
bureau de
cases.
Mais dans tous les cas, l'adresse de la variable est l'adresse de la première de ces cases.
A un paramètre formel de subroutine ou de fonction est associé en mémoire un nombre de cases suffisant pour stocker une adresse (4 octets sous UNIX). Lors d'un appel à une subroutine ou fonction, l'adresse du premier argument est écrite à l'emplacement réservé au premier paramètre formel, idem pour le second, le troisième, etc.
La figure 10.6 illustre ceci dans l'exemple de la subroutine
polar
.
Lorsque dans le corps de la subroutine, le programme rencontrera le paramètre
formel x
de type double precision à droite de =
, il ira
lire 8 octets à partir de l'adresse contenue dans x
(de 10000 à 10007),
c'est-à-dire la valeur de a
.
Lorsqu'il rencontrera une affectation du paramètre formel theta
, il
ira écrire les 8 octets à partir de l'adresse contenue dans theta
(de 10024 à 10031), c'est-à-dire phi
.
D'où l'importance de respecter le nombre et le type d'arguments.
common
On a vu que par défaut les variables d'un bloc fonctionnel lui étaient
locales, donc inconnues des autres blocs. Il existe un moyen d'étendre la
portée d'une variable à plusieurs blocs fonctionnels : le common
.
Un common
comporte un nom et une liste de variables. Les variables de
cette liste seront connues dans tous les blocs fonctionnels où l'on
écrit le common
.
|
common
ne dispense pas des déclarations.
parameter
dans
les common
s (en particulier les tailles de tableaux)
common
s différents.
common
.
common
, à condition de les déclarer de la
même longueur partout.
common
est la même dans tous les blocs fonctionnels où il
apparaît. On a donc le droit de changer le nom des variables entre deux
utilisations d'un même common
, mais cela est déconseillé.
|
Dans l'exemple de la figure 10.7, on voit que les noms des
variables du common
bidon
sont différentes dans le
programme principal et dans truc.
Mais cela est correct puisqu'il y a 2 real
s de chaque coté.
u
et a
représentent exactement le même objet car ils
correspondent à la même zone mémoire. Cela dit, il vaut mieux garder les mêmes
noms partout.
On peut passer des tableaux à des subroutines ou fonctions si celles-ci sont conçues pour les recevoir. Comment déclare-t-on des paramètres formels de type tableaux ? Cela dépend du nombre d'indices.
v
(1 indice)
v
:
|
m
(2 indices)
|
Ces déclarations s'appliquent uniquement aux paramètres formels, qui
rappelons-le ne sont que des portes de communication pour les subroutines et
les fonctions. En aucun cas une variable ne pourra être déclarée avec une
* . Une variable de type tableau (rappel) doit toujours être
déclarée avec des constantes entières
|
Au vu du mécanisme de passage des arguments aux subroutines, expliquez pourquoi les paramètres formels de type vecteurs et matrices sont déclarés de cette manière.
Subroutine qui lit une matrice au clavier. La subroutine devra sortir la matrice, son nombre de lignes réelles, son nombre de colonnes réelles (figure 10.8).
Il faut bien comprendre que la seule matrice ayant une existence réelle est la
matrice mat
du programme principal, et que pour lui réserver de la
mémoire, il faut la déclarer avec un nombre de lignes et un nombres de
colonnes explicites.
|
Le paramètre formel a
de la subroutine va recevoir l'adresse de
mat
au moment de l'appel, et connaissant sa première taille de
déclaration (10) via le paramètre formel ma
, elle sera à même de
lire et écrire un élément quelconque de mat
.
Que se passe-t-il si l'utilisateur tape un nombre de lignes supérieur à 10 et/ou un nombre de colonnes supérieur à 20 ?
Écrire une subroutine qui affiche une matrice à l'écran, et combinez-la avec
litmat
pour vérifier votre réponse à la question précédente.
external
Utiliser le nom d'une fonction ou d'une subroutine comme argument d'une autre fonction ou subroutine.
Quelle drôle d'idée ?
Examinons le problème suivant : Écrire une subroutine qui calcule l'intégrale pour une fonction f quelconque. Que faudra-t-il faire entrer et sortir de la subroutine ?
Or quel est le moyen en FORTRAN de programmer une fonction f(x) ?
C'est d'utiliser une fonction FORTRAN, qui renverra par exemple une
valeur real
.
On peut donc prédire la forme de la subroutine d'intégration :
subroutine integ (a, b, f, valint) |
Les paramètres formels a
, b
et valint
seront
double precision
, mais de quel type est f
?
C'est le nom d'une fonction FORTRAN, et pour déclarer un paramètre formel aussi bizarre, on écrira :
external f |
et aussi
real f |
car f
renvoie une valeur real
. On peut aussi déclarer des
paramètres formels de type subroutine, en utilisant simplement
external
. Dans le corps de la subroutine, on fera bien sûr appel à
cette fonction f
pour calculer l'intégrale. Pas de difficulté ! On
fait comme si elle existait et on écrira des choses du style :
valint = valint + h/2 *(f(x1+h) + f(x1)) |
Maintenant ma subroutine d'intégration est écrite. Je souhaite l'appliquer à une fonction que j'ai écrite en FORTRAN, du style :
real function truc(x)
|
Je veux appeler la subroutine integ
pour calculer l'intégrale de
cette fonction entre disons 1 et 2. J'écrirai :
call integ (1.0, 2.0, truc, somtruc) |
1.0
et 2.0
sont des constantes real
,
somtruc
une variable real
qui me renverra la valeur de
l'intégrale...
Et truc
? C'est le nom d'une fonction FORTRAN. Ai-je le droit de
passer ce genre de chose en argument à une subroutine ? La réponse est oui, si
je la déclare :
external truc |
dans le bloc fonctionnel d'où je fais le call
. De plus, comme cette fonction
renvoie une valeur real
, j'ajouterai :
real truc |
La figure 10.9 récapitule tout ce que l'on vient d'expliquer.
|
Remarquons que la structure de truc
est imposée par la subroutine
qui demande que la fonction représentée par son paramètre formel f
ait un argument réel et renvoie une valeur réelle. Nous ne pourrions donc pas
par exemple déclarer :
real function truc(x,y) |
En général, les subroutines du type integ
sont des boites noires
toutes faites (par des spécialistes), et on vous indique juste le type de ses
paramètres formels. Lorsque l'un d'entre eux est une fonction ou une
subroutine, on vous indique en plus la liste des paramètres formels que doit
avoir cette fonction ou subroutine.
Écrire la structure d'un programme (programme principal / subroutine / fonctions) pour trouver les zéros d'une fonction f(x) par la méthode de Newton. On rappelle que cette méthode nécessite la connaissance de la fonction f(x) et de sa dérivée f'(x).