Quelques exemples de prototypes de fonctions:
// Fonction Carre () // Intrants: Nombre (un entier) // Extrants: un entier (le carré de Nombre) // Rôle: Calcule et retourne le carré du nombre Nombre int Carre (int Nombre); // Fonction Puissance () // Intrants: Nombre (un réel) // Exposant (un entier) // Extrants: un réel (Nombre élevé à la puissance Exposant) // Rôle: Calcule et retourne Nombre élevé à la puissance // Exposant float Puissance (float Nombre, int Exposant); // Fonction ValeurAbsolue() // Intrants: Nombre (un entier) // Extrants: un entier (la valeur absolue de Nombre) // Rôle: Calcule et retourne la valeur absolue de Nombre int ValeurAbsolue (int Nombre); |
Quelques exemples de prototypes de procédures:
// Procédure Dessiner_Ligne () // Intrants: Nombre_Car (un entier), le nombre de caractères // à dessiner // Symbole (un caractère), le caractères à dessiner // Extrants: aucun // Rôle: Affiche une ligne faite de Nombre_Car fois le // caractère Symbole void Dessiner_Ligne (int Nombre_Car, char Symbole); // Procédure Afficher_Menu() // Intrants: aucun // Extrants: aucun // Rôle: Affiche le menu principal de l'application void Afficher_Menu (); |
Définition |
Le prototype d'un sous-programme informe le programme de l'existence de ce sous-programme, et de sa signature (nom du sous-programme, nombre de paramètres, type des paramètres). On peut dire que le prototype décrit le sous-programme. |
La syntaxe propre à la définition d'un sous-programme est la suivante:
Voici un exemple de définition de la fonction «Carre()»:
// Fonction Carre () // Intrants: Nombre (un entier) // Extrants: un entier (le carré de Nombre) // Rôle: Calcule et retourne le carré du nombre Nombre int Carre (int Nombre) { int Resultat; Resultat = Nombre * Nombre; return Resultat; } // Carre (int) |
Remarquez que le type de la variable «Resultat» («int») correspond au type de la fonction. L'énoncé «return» fera en sorte d'attribuer à la fonction «Carre()» la valeur se trouvant dans la variable «Resultat» au moment de l'énoncé «return».
Note: | il existe plus d'une façon d'écrire un même sous-programme. Essayez d'écrire la fonction «Carre()» ci-dessus de manière différente... |
Soit le programme C++ suivant:
#include <iostream> using namespace std; // Fonction Carre () // Intrants: Nombre (un entier) // Extrants: un entier (le carré de Nombre) // Rôle: Calcule et retourne le carré du nombre Nombre int Carre (int Nombre); // prototype de la fonction // Programme principal // Lire un entier, calcule son carré, et l'affiche void main () { int Valeur, // pour lire l'entrée faite par l'usager Carre_De_Valeur; // pour saisir le résultat du traitement cout << "Veuillez entrer un nombre s.v.p.: "; cin >> Valeur; Carre_De_Valeur = Carre (Valeur); // appel de la fonction cout << "Le carré de " << Valeur << " est: " << Carre_De_Valeur << endl; } // programme principal // Fonction Carre () // Intrants: Nombre (un entier) // Extrants: un entier (le carré de Nombre) // Rôle: Calcule et retourne le carré du nombre Nombre int Carre (int Nombre) { int Resultat; Resultat = Nombre * Nombre; return Resultat; } |
Présumant que l'usager entre la valeur «6»
au «cin>> Valeur;»,
voici concrètement ce qui se produira lors de l'appel de la fonction
«Carre()».
Valeur
|
Carre_de_Valeur
|
|
---|---|---|
Présumant toujours que l'usager ait entré
la valeur «6» à la
suite de l'invitation «Veuillez entrer
un nombre s.v.p.», les valeurs des variables
déclarées dans le bloc d'instruction du
«main()» auront
respectivement comme valeur, au moment de l'appel:
|
6
|
?
|
Une fonction est un sous-programme qui a de particulier qu'il accomplit une tâche--que l'on veut précise et bien définie--et prend la valeur de son résultat.
Définition |
Lorsqu'un sous-programme (comme le «main()») a recours aux services d'une fonction, on dit que celui-ci appelle la fonction. |
Dans un morphogramme, on représentera une fonction comme s'imbriquant dans une séquence en tant qu'opération (comprenant une affectation, puisqu'elle produit un résultat d'un type donné).
La fonction est toutefois une opération dite complexe, qu'il faut éventuellement détailler; ce n'est pas une opération simple ou élémentaire comme le sont la lecture, l'écriture et l'affectation. Une fonction n'est pas une primitive.
L'appel de fonction a donc pour effet de créer un saut dans le programme, du bloc d'instructions de l'appelant (le «main()» ici) au bloc d'instructions de la fonction appelée. Schématiquement:
Il est important à ce stade-ci de parler de la portée des variables. Une variable n'existe que de la ligne à laquelle elle est déclarée à l'accolade fermante du bloc dans lequel elle est déclarée, inclusivement. On dit qu'elle est locale à ce bloc.
Ainsi, les variables déclarées dans le sous-programme «main()» sont locales à ce sous-programme, et n'existent pas pour la fonction «Carre()»; cette dernière ne pourrait donc pas accéder à la variable «Valeur», par exemple, qui n'existe que pour la procédure «main()».
De la même manière, les variables déclarées dans la fonction «Carre()» n'existent que pour cette fonction. Ainsi, le «main()» ne pourrait pas utiliser la variable «Resultat» de la fonction «Carre()», car pour lui, cette variable n'existe pas.
Note: | pour la même raison, si le «main()» déclarait une variable nommée «Variable_Drole» et la fonction «Carre()» déclarait aussi une variable nommée «Variable_Drole», il y aurait deux variables différentes du même nom, mais aucun risque de conflit, car le «main()» ne connaîtrait que sa propre «Variable_Drole», et il en serait de même pour la fonction «Carre()». |
Puisqu'un sous-programme n'a pas accès aux variables locales d'un autre sous-programme, il faut un mécanisme pour que les sous-programmes puissent «se parler», échanger des données.
Dans le cas de l'appel par «main()» de «Carre()», il faut un mécanisme pour que le «main()» puisse indiquer à la fonction «Carre()» la valeur dont il désire connaître le carré.
Bien entendu, ce mécanisme de communication existe; c'est ce qu'on nomme la mécanique du passage de paramètres.
Définition |
Le passage de paramètres est le mécanisme par lequel un sous-programme appelant informe le sous-programme appelé des données avec lesquelles ce dernier devra travailler[2]. |
Regardons un peu comment cela fonctionne:
Il est important de relever que ce n'est que la valeur de la variable «Valeur» qui est copiée dans la variable «Nombre» lors du passage de paramètre; le paramètre «Nombre» du sous-programme «Carre()» deviendra une copie de la variable «Valeur» du sous-programme «main()».
Si la fonction «Carre()»
modifiait le contenu de la variable «Nombre»,
cela ne changerait en rien le contenu de la variable «Valeur»
du sous-programme «main()».
Ce sont deux variables différentes, bien qu'elles contiennent à
un certain moment deux copies distinctes d'une même valeur.
Lorsque la fonction termine son exécution, elle doit souligner au sous-programme l'ayant appelé du résultat qu'elle a produit. On dira alors que la fonction prend une valeur--celle de son résultat--ou alors qu'elle retourne une valeur.
En C++, l'énoncé signifiant la fin de l'exécution d'une fonction et la production de son résultat utilise le mot clé «return».
L'énoncé «return» en C++ fait en sorte qu'une valeur soit attribuée à la fonction. Par exemple, pour la fonction «Carre()», nous avons:
Poursuivant notre exemple, puisque «Nombre» s'était vu confier, à l'appel de la fonction, la valeur «6», la variable «Resultat» contiendra la valeur «36» (résultat de l'opération «Nombre * Nombre»).
À ce moment, l'appel «Carre(Valeur)» (donc «Carre(6)») du «main()» prend donc la valeur «36». L'affectation «Carre_De_Valeur = Carre (Valeur);» fait en sorte que la valeur «36», résultat de cet appel à «Carre()» soit affectée à la variable «Carre_De_Valeur».
Le schéma suivant montre le chemin que prend la valeur de retour d'une fonction: