Académique Documents
Professionnel Documents
Culture Documents
Le C++ est un langage procédural (entre autres paradigmes), on peut définir des fonctions qui vont
effectuer une certaine tâche. On peut paramétrer des fonctions qui vont permettre de paramétrer cette tâche
et rendre ainsi les fonctions réutilisables dans d'autres contextes.
#include <iostream>
using namespace std;
int main()
{
int nombre = 21;
cout << doubleur(nombre) << endl; // 42.
return 0;
}
Le prototype va préciser le nom de la fonction, donner le type de la valeur de retour de la fonction (void
quand il n'y a pas de retour), et donner les types des paramètres éventuels, ainsi que leurs éventuelles
valeurs par défaut. Le prototype d'une fonction est facultatif. Si on ne met pas de prototype, on devra
rédiger la fonction avant le main, au lieu de la rédiger après ou dans un fichier séparé (extension .h).
syntaxe
type identificateur(paramètres);
Exemple
// prototype de la fonction f :
double f(double x,double y);
Rôle
Le rôle d'un prototype n'est pas de définir les instructions de la fonction, mais donner sa signature. Il est
utilisé pour spécifier que la fonction existe, et est implémentée ailleurs (dans un autre fichier, une librairie,
ou à la fin du fichier source).
La définition va reprendre le prototype mais va préciser cette fois le contenu de la fonction (le corps).
Syntaxe
type identificateur(paramètres)
{
... Liste d'instructions ...
}
Exemple
#include <iostream>
using namespace std;
// définition de la fonction f :
double f(double x, double y)
{
double a;
a = x*x + y*y;
return a;
}
int main()
{
double u, v, w;
cout << "Tapez la valeur de u : "; cin >> u;
cout << "Tapez la valeur de v : "; cin >> v;
#include <iostream>
using namespace std;
// prototype de la fonction f :
double f(double x, double y);
int main()
{
double u, v, w;
cout << "Tapez la valeur de u : "; cin >> u;
cout << "Tapez la valeur de v : "; cin >> v;
// définition de la fonction f :
double f(double x, double y)
{
double a;
a = x*x + y*y;
return a;
}
Dans cet exemple, le prototype est nécessaire, car la fonction est définie après la fonction main qui
l'utilise. Si le prototype est omis, le compilateur signale une erreur.
Présentation
On appelle environnement d'une fonction l'ensemble des variables auxquelles elle peut accéder. Les
différents environnements sont donc largement séparés et indépendants les uns des autres. Cette séparation
permet de mieux structurer les programmes.
Exemple
1 #include <iostream>
2 using namespace std;
3
4 int b; // variable globale
5
6 double f(double x, double y)
7 {
8 double a; // variable locale à la fonction f
9 a = x*x + y*y;
10 return a;
11 }
12
13 double g(double x)
14 {
15 int r, s, t; // variables locales à la fonction g
16 /* ... */
17 }
18
19 int main()
20 {
21 double u, v, w; // variables locales à la fonction main
22 /* ... */
23 return 0;
24 }
b est une variable globale (à éviter : une structure la remplace avantageusement !).
f peut accéder
à ses paramètres x et y.
à sa variable locale a.
à la variable globale b.
g peut accéder
à son paramètre x.
à ses variables locales r,s et t.
à la variable globale b.
la fonction main peut accéder
à ses variables locales u,v et w.
à la variable globale b.
Passer un paramètre par pointeur permet de modifier la valeur pointée en utilisant l'opérateur de
déréférencement *.
Pour passer un pointeur comme argument de fonction, il faut en spécifier le type dans la définition de la
fonction et (si pertinent), dans le prototype de la fonction, comme ceci :
De même, il faut, lors de l'appel de fonction, non pas spécifier le nom de la variable comme lors d'un
passage par valeur, mais son adresse. Pour ceci, il suffit de placer le signe & devant la variable.
Notez donc le & devant la variable, ceci a pour effet de passer l'adresse mémoire de la variable.
int main()
{
int a = 5;
int b = 7;
system ("PAUSE");
return 0;
}
On affiche les deux variables puis on appelle les deux fonctions, une par valeur et une par pointeur. Puis on
affiche dans ces deux fonctions la valeur et on modifie la valeur. De retour dans main, on réaffiche les deux
variables a et b. a, qui a été passée par valeur, n'a pas été modifiée et a toujours sa valeur initiale (5),
spécifiée à l'initialisation. Or, b n'a plus la même valeur que lors de son initialisation, 7, mais la valeur de 9.
En effet, lors d'un appel par valeur, une copie de cette valeur est créée, donc lorsqu'un appel de fonction par
valeur est effectué, on ne modifie pas la valeur d'origine mais une copie, ce qui fait que lorsqu'on retourne
dans la fonction d'origine, les modifications effectuées sur la variable dans la fonction appelée ne sont pas
prises en comptes. En revanche, lors d'un appel par pointeur, il s'agit de la variable elle même qui est
modifiée (puisqu'on passe son adresse). Donc si elle est modifiée dans la fonction appelée, elle le sera
également dans la fonction appelante.
Les avantages sont que cet appel nécessite moins de charge de travail pour la machine. En effet, par valeur,
il faut faire une copie de l'objet, alors que par pointeur, seule l'adresse de l'objet est copiée. L'inconvénient,
c'est qu'on peut accidentellement modifier dans la fonction appelée la valeur de la variable, ce qui se
répercutera également dans la fonction appelante. La solution est simple : il suffit d'ajouter const dans le
prototype de fonction de cette manière :
En recompilant le programme mis plus haut avec ces deux choses, on constate un message d'erreur
indiquant que l'on n'a pas le droit de modifier une valeur constante.
Passer un paramètre par référence a les mêmes avantages que le passage d'un paramètre par pointeur. Celui-
ci est également modifiable. La différence est que l'opérateur de déréférencement n'est pas utilisé, car il
s'agit déjà d'une référence.
Le passage de paramètre par référence utilise une syntaxe similaire au passage par pointeur dans la
déclaration de la fonction, en utilisant & au lieu de *.
Par exemple :
void test()
{
int a = 5;
cout << "a = " << a << endl; // a = 5
incrementer(a);
Le paramètre ainsi passé ne peut être qu'une variable. Sa valeur peut être modifiée dans la fonction appelée,
à moins d'utiliser le mot const :
void test()
{
int a = 5;
cout << "a = " << a << endl;
incrementer(a);
cout << "a = " << a << endl;
}
La question que l'on peut se poser est puisque le passage par référence permet de modifier le paramètre,
pourquoi l'en empêcher, et ne pas utiliser un simple passage par valeur ? La réponse se trouve lorsqu'on
utilise des objets ou des structures. Le passage par valeur d'un objet ou d'une structure demande une recopie
de la valeur de ses membres (utilisant un constructeur de recopie pour les objets). L'avantage de passer une
référence est d'éviter la recopie. Le mot const permet de garantir qu'aucun membre de l'objet ou de la
structure n'est modifié par la fonction.
Pointeur de fonction
Un pointeur de fonction stocke l'adresse d'une fonction, qui peut être appelée en utilisant ce pointeur.
La syntaxe de la déclaration d'un tel pointeur peut paraître compliquée, mais il suffit de savoir que cette
déclaration est identique à celle de la fonction pointée, excepté que le nom de la fonction est remplacé par
(* pointeur).
Obtenir l'adresse d'une fonction ne nécessite pas l'opérateur &. Il suffit de donner le nom de la fonction seul.
Exemple :
#include <iostream>
#include <iomanip>
int main()
{
pf_comparateur = &compare_prix;
cout << "compare 1 et 2 : " << (*pf_comparateur)(1, 2) <<
endl;
return 0;
}
La syntaxe d'appel à une fonction par un pointeur est identique à l'appel d'une fonction classique.
Exemple :
Exercices
Exercice 1
Créez une fonction factorielle pour calculer la factorielle d'un entier n. Elle retourne un entier long.
Créez une fonction non récursive.
Aide
Solution
Voici le code source:
long factorielle(int n)
{
long r = 1;
while (n-- > 0)
r *= n;
return r;
}
Exercice 2
Aide
Solution
Voici le code source:
long factorielle(long n)
{
if (n == 1) return 1;
return n*factorielle(n - 1);
}
Exercice 3
Solution
Voici le code:
long (*fct)(long);
Exercice 4
En C++, les systèmes callback sont des systèmes qui enregistrent des fonctions, afin de les exécuter en
cas d'évènement (comme par exemple si on touche le clavier).
Albert n'est pas fort en maths. Il veut un système pour faire des racines carrées, des carrés, des cosinus et
des factorielles. Il utilise l'environnement callback situé dans l'annexe 1. Cet environnement crée une
structure f_maths, qui enregistre une fonction et son symbole mathématique. Pour utiliser sa calculatrice, il
doit enregistrer correctement les fonctions dans le gestionnaire. Ensuite, le programme interroge l'utilisateur
pour entrer le symbole de son opération. Puis, l'utilisateur entre sa valeur et le programme effectue
l'opération. Compléter le programme en annexe avec la fonction main() uniquement.
Annexe 1
Système :
#include <cmaths>
#include <stdio.h>
f_maths gestionnaire[10];
double factorielle(double n)
{
if (n == 1) return 1;
return n*factorielle(n - 1);
}
void enregistrer(fct f, char sy)
{
struct s;
s.fonction = f;
s.symb = sy;
gestionnaire[nb_fct++] = s;
}
void executer()
{
char sy;
double val;
printf("Entrer le symbole:\n");
scanf("%c", &sy);
printf("Entrer la valeur:\n");
scanf("%g", &val);
for(int i = 0; i < nb_fct; i++)
if (gestionnaire[i].symb == sy)
{
printf("Le résultat est %g.\n", (*gestionnaire[i].fonction)
(val);
return;
}
printf("Aucune fonction trouvée.\n");
}
Aide
Pour faire des cosinus et des racines carrées, cmaths contient les fonctions cos() et sqrt().
Voir aussi
Exercices
Récupérée de « https://fr.wikibooks.org/w/index.php?
title=Programmation_C%2B%2B/Les_fonctions&oldid=671604 »
Les textes sont disponibles sous licence Creative Commons attribution partage à l’identique ; d’autres termes
peuvent s’appliquer.
Voyez les termes d’utilisation pour plus de détails.