Les fonctions
Pourquoi les fonctions? Besoin de regrouper un ensemble d’instructions pour décomposer la complexité d’un programme, De même, il peut être nécessaire de décomposer ce programme en modules effectuant un travail une fois pour toute (par exemple la multiplication de deux matrices, un tri, un algorithme d’identification, ...tc) Il peut arriver aussi qu’un même traitement intervienne plusieurs fois dans un programme.
Dans ce cas, il est intéressant de pouvoir faire appel plusieurs fois au même module. Ces notions sont le plus souvent recouvertes par le sous concept de sous-programmes. En C, ces sous-programmes sont appelés FONCTIONS.
Il existe deux types de fonctions: Les fonctions pré-définies Nos propres fonctions
Les fonctions pré-definies La plupart des environnements supportant le C standard disposent d'une large collection de fonctions appelée bibliothèque standard. Plusieurs fonctions mathématiques courantes font parties de la bibliothèque standard. Pour les utiliser, il suffit d'inclure au début du programme la ligne suivante: #include <math.h> Il est alors possible d'employer des fonctions telles que:
cos(x) cosinus de x pow(x,y) x à la puissance y sqrt(x) racine carrée de x ldexp(x,n) …… et plusieurs autres.
Exemple 1: cos(x) #include <iostream.h> #include <math.h> main(){ double x; cin >> x; cout << cos(x); }
Exemple: sqrt(x) Il est bien connu que l'équation où a,b et c sont des constantes, possède deux solutions données par la formule Il est donc utile d'utiliser la fonction sqrt pour écrire un programme calculant les racines d'un polynôme.
Exemple: sqrt(x) #include <iostream.h> #include <math.h> main(){ double a, b, c; double racine1, racine2; double delta; cin>>a>>b>>c; delta = sqrt(b*b - 4*a*c); if (delta >=0) { racine1 = ( -b - delta) / (2*a); racine2 = ( -b + delta) / (2*a); cout<<racine1<<endl<<racine2<<endl; } else cout << ‘’pas de solution dans les réels’’; return (0);
Exemple: ldexp(x,n) Examinons plus en détails la fonction ldexp. Rappelons que cette fonction retourne la valeur Considérons quelques exemples: ldexp(1,1) retourne ldexp(1.1, 1) retourne ldexp(1,1.1) retourne Pourquoi???
Exemple: ldexp(x,n) Pour comprendre ce qui se passe il faut savoir que la fonction ldexp attend deux nombres en entrée: x et n. x doit être un double n doit être un entier la fonction retourne un double Donc lorsque l'on exécute ldexp(1, 1.1), la fonction reçoit en fait les valeurs 1 et 1 puisque la partie fractionnaire du second opérande est tronquée.
Les prototypes de fonctions Morale: avant d'utiliser une fonction de la bibliothèque il faut connaître son prototype. Le prototype d'une fonction indique le nom de la fonction le type des paramètres le type de la valeur de retour Exemple: double ldexp(double, int) indique que ldexp est une fonction à deux paramètres (un double et un int) qui retourne un double.
double pow(double x, double y) Exemple: pow(x,y) La fonction d'exponentiation a le prototype suivant: double pow(double x, double y) de sorte qu'on ne peut pas l'utiliser de la façon suivante: pow(2, 3) % 5 Pourquoi?
Exemple: pow(x,y) Il faut plutôt écrire: (int) pow(2, 3) % 5 ou encore L'opérateur cast permet de convertir le type d'une expression en un autre type. (type) expression
Exemple: pow(x,y) Remarque On voit ici une différence fondamentale entre le pseudo-code et un vrai langage de programmation tel que le C. En pseudo-code, on n'a pas à se soucier des détails particuliers à un langage. Par exemple, on écrit: x modulo 5 sans que cela ne cause de problème. Cela permet de se concentrer sur l'algorithme plutôt que sur la syntaxe.
Une liste de quelques de fonctions mathématiques ceil(x) retourne la valeur entiere par excès de x floor(x) retourne la valeur entiere par défaut de x fabs(x) retourne la valeur aboslue dun valeur réelle x log(x) retourne le logaritme népérein de x log10(x) retourne le logaritme décimal de x sin(x) retourne le sinus de x sqrt(x) retourne la racine carrée x tan(x) retourne la tangente de x Vérifier à chaque fois leur prototype
Structure d'un programme en C Un programme en C est une collection de fonctions qui interagissent entre elles afin d'exécuter un calcul. Une des fonctions doit porter le nom main: c'est la première fonction à être appelée lors de l'exécution du programme. Comme on l'a vu, on peut utiliser les fonctions de la bibliothèque standard ou encore, on peut définir nos propres fonctions
{ { Définition des fonctions Dans la plupart des langages de programmation, la définition d'une fonction comporte deux parties: l'entête et le corps. { Nom de la fonction Type de la valeur retournée Nom des paramètres Types des paramètres Entête { Déclaration des variables Instructions Corps
Étude de cas Calcul de la moyenne Description du problème: Étant donné 3 entiers, calculer leur moyenne sous forme d'entier tronqué et afficher le résultat. Entrée: Trois entiers séparés par un ou plusieurs caractères d'espacement. Sortie: Un entier représentant la partie entière de la moyenne.
entier moyenne3(entier, entier, entier) Calcul de la moyenne Supposons l'existence d'une fonction ayant le prototype suivant: entier moyenne3(entier, entier, entier)
Fonction principale entier nombre1 nombre2 nombre3 entier moyenne lire nombre1 nombre2 nombre3 moyenne ¬ moyenne3(nombre1, nombre2, nombre3) écrire moyenne
Définition de la fonction moyenne3 Entête: entier moyenne3( entier a, entier b, entier c) Corps: entier m m¬ (a + b + c) / 3 retourner m
Définition de la fonction moyenne3 Entête: entier moyenne3(entier a, entier b, entier c) Corps: entier m m¬ (a + b + c) / 3 retourner m À l'appel de la fonction, les variables a,b et c sont déclarées puis initialisées avec la valeur des paramètres utilisés lors de l'appel.
Vérifions le comportement de notre algorithme sur l'exemple Pas-à-pas Vérifions le comportement de notre algorithme sur l'exemple suivant: 67 86 79 nombre1 entier nombre2 entier nombre3 entier moyenne entier Après les déclaration suivantes: entier nombre1 nombre2 nombre3 entier moyenne l'état de la mémoire correspond à la figure de droite. . .
lire nombre1 nombre2 nombre3 67 entier nombre2 86 entier nombre3 79 entier moyenne entier lire nombre1 nombre2 nombre3 . .
L'analyse de l'instruction moyenne ¬ moyenne3(nombre1, nombre2, nombre3) est plus compliquée. On doit d'abord évaluer l'expression moyenne3(nombre1, nombre2, nombre3) Pour ce faire, il faut remplacer les variables par leur valeur respective: moyenne3(67, 86, 79) Puis, la fonction moyenne3 est appelée.
Appel de moyenne3 Lorsqu'une fonction est appelée, chacun de ses paramètres est copié dans une nouvelle variable. Le nom de ces nouvelles variables correspond au nom des paramètres tels qu'indiqué dans l'entête. paramètres d’appel moyenne3(nombre1, nombre2, nombre3) moyenne3( 67 , 86 , 79 ) moyenne3( a , b , c ) paramètres formels
l'état de la mémoire devient: nombre1 67 Ainsi après l'appel de moyenne3(67, 86, 79) l'état de la mémoire devient: entier nombre2 86 entier nombre3 79 entier moyenne entier a 67 entier Remarquer que les variables formelles a, b et c sont créées et initialisées à 67, 86 et 79. b 86 entier c 79 entier . .
Exécution du corps de moyenne3 entier m m¬ (a + b + c) / 3 retourner m D 'abord, la déclaration nombre1 67 entier nombre2 86 entier nombre3 79 entier moyenne entier a 67 entier b 86 entier c 79 entier m entier . .
Exécution du corps de moyenne3 entier m m¬ (a + b + c) / 3 retourner m Puis l'évaluation de l'expression (a + b + c) / 3 = (67 + 86 + 79) / 3 = (153 + 79) / 3 = 232 / 3 = 77.33 nombre1 67 entier nombre2 86 entier nombre3 79 entier moyenne entier a 67 entier b 86 entier c 79 entier m entier . .
Exécution du corps de moyenne3 entier m m¬ (a + b + c) / 3 retourner m Et l'assignation m ¬ 77.33 Remarquez que 77 qui est mis dans m et non pas 77.33. nombre1 67 entier nombre2 86 entier nombre3 79 entier moyenne entier a 67 entier b 86 entier c 79 entier m 77 entier . .
Exécution du corps de moyenne3 entier m m¬ (a + b + c) / 3 retourner m Finalement, la valeur souhaitée est retournée à l'aide de l'instruction spéciale: nombre1 67 entier nombre2 86 entier nombre3 79 entier moyenne entier a 67 entier b 86 entier c 79 entier m 77 entier . .
variables contenants les paramètres disparaissent. nombre1 67 entier nombre2 86 entier Au sortir d'une fonction, toutes les variables qui y ont été déclarées ainsi que toutes les variables contenants les paramètres disparaissent. nombre3 79 entier moyenne entier . .
Retour à la fonction principale: lire nombre1 nombre2 nombre3 moyenne ¬ moyenne3(nombre1, nombre2, nombre3) écrire moyenne Puisque la valeur retournée par moyenne3 est 77, cela veut dire que moyenne3(67, 86, 79) est une expression s'évaluant à 77.
La prochaine instruction à être exécutée est donc moyenne ¬ 77 nombre1 67 entier La prochaine instruction à être exécutée est donc moyenne ¬ 77 nombre2 86 entier nombre3 79 entier moyenne 77 entier Finalement, l'instruction écrire moyenne est exécutée . .
Calcul de l'aire d'un rectangle Description du problème: Calculer et afficher l'aire d'un rectangle. Entrée: Deux nombres réels, le premier indique la longueur et le second indique la largeur du rectangle. Sortie: Un nombre réel représentant l'aire du rectangle.
Fonction principale réel longueur largeur lire longueur largeur écrire aire(longueur, largeur) où aire est une fonction dont le prototype est réel aire(réel, réel)
Définition de aire Entête: réel aire(réel longueur, réel largeur) Corps: retourner longueur * largeur
Vérifions le comportement de notre algorithme sur l'exemple Pas-à-pas Vérifions le comportement de notre algorithme sur l'exemple suivant: 7.2 3.5 longueur réel largeur réel Après les déclaration suivantes: réel longueur largeur l'état de la mémoire correspond à la figure de droite. . .
longueur 7.2 réel largeur 3.5 réel lire longueur largeur . .
écrire aire(longueur, largeur) D'abord il faut évaluer On appelle donc la fonction aire. longueur 7.2 réel largeur 3.5 réel . .
Appel de la fonction aire Puisque l'entête de la fonction spécifie que les nom des paramètres sont longueur et largeur, cela veut dire que deux nouvelles variables portant ces noms et contenant les mêmes valeurs sont créées. longueur 7.2 réel largeur 3.5 réel longueur 7.2 réel largeur 3.5 réel . .
Les instructions du corps de l;a fonction sont ensuite exécutées. retourner longueur * largeur = retourner 7.2 * 3.5 = retourner 25.2 longueur 7.2 réel largeur 3.5 réel longueur 7.2 réel largeur 3.5 réel . .
Les instructions du corps sont ensuite exécutées. retourner longueur * largeur = retourner 7.2* 3.5 = retourner 25.2 longueur 7.2 réel largeur 3.5 réel longueur 7.2 réel largeur 3.5 réel Comment savoir à quelle case mémoire longueur et largeur correspondent? . .
dans une fonction n'est visible que dans cette fonction. Déclarés et visibles seulement dans la fonction principale longueur 3.5 réel largeur 7.2 réel Déclarés et visibles seulement dans la fonction aire longueur 3.5 réel largeur 7.2 réel . . Une variable déclarée dans une fonction n'est visible que dans cette fonction.
Au retour de la fonction, les variables qui y ont été créées disparaissent. L'expression aire(7.2, 3.5) vaut la valeur retournée, c'est-à-dire 25.2. longueur 7.2 réel largeur 3.5 réel . .
Retour dans la fonction principale réel longueur largeur lire longueur largeur écrire aire(longueur, largeur) longueur 3.5 réel largeur 7.2 réel L'instruction écrire aire(longueur, largeur) correspond à écrire 25.2 Ce qui est bien ce que l'on voulait que notre programme fasse! . .
Échanger deux valeurs Description du problème: Lire deux entiers, les afficher dans l'ordre où ils ont été lus puis, les afficher dans l'ordre inverse. Utiliser une fonction pour inverser le contenu de deux variables. Entrée: Deux entiers. Sortie: Les deux même entiers, dans l'ordre inverse.
Fonction principale 1 entier x y lire x y écrire x y écrire y x Mais cela ne satisfait pas la description du problème
Fonction principale 2 entier x y lire x y ecrire x y echanger(x, y) où echanger est une fonction dont le prototype est: echanger(entier, entier) Remarque: la fonction echanger ne retourne aucune valeur.
Définition 1 de echanger Entête: echanger(entier a, entier b) Corps: a b b a
Pas-à-pas Entrée: 10 15 entier x y lire x y 10 15 . . x entier y
écrire x y échanger(x, y) 10 15 10 15 . . x entier y entier a entier b
échanger(entier a, entier b) a ¬ b b ¬ a x 10 entier y 15 entier a 15 entier b 15 entier . . On exécute d ’abord a ¬ b
échanger(entier a, entier b) a ¬ b b ¬ a x 10 entier y 15 entier a 15 entier b 15 entier Puis on exécute b ¬ a Problème!!! . .
Définition 2 de échanger Entête: échanger(entier a, entier b) Corps: entier tmp tmp ¬ a a ¬ b b ¬ tmp x 10 entier y 15 entier a 10 entier b 15 entier . .
entier tmp tmp ¬ a a ¬ b b ¬ tmp 10 15 10 15 . . x entier y entier a
entier tmp tmp ¬ a a ¬ b tmp ¬ a b ¬ tmp 10 15 10 15 10 . . x entier y
entier tmp tmp ¬ a a ¬ b b ¬ tmp a ¬ b 10 15 15 15 10 . . x entier y
entier tmp tmp ¬ a a ¬ b b ¬ tmp b ¬ tmp 10 15 15 10 10 . . x entier y
État de la mémoire au retour de la fonction echanger: x 10 entier y 15 entier État de la mémoire au retour de la fonction echanger: On a échangé le contenu des variables a et b mais pas celui des variable x et y!!! . .
Définition 3 de échanger Entête: echanger(entier référence a, entier référence b) où a et b ne sont pas des copies mais des références aux paramètres d'appel. Corps: entier tmp tmp ¬ a a ¬ b b ¬ tmp
paramètres passés par copie paramètres formels: a, b x paramètres passés par copie paramètres formels: a, b paramètres d'appel: x, y y a b . .
paramètres passés par référence paramètres formels: a, b a º x paramètres passés par référence paramètres formels: a, b paramètres d'appel: x, y b º y . .
entier x y lire x y Pas-à-pas Entrée: 10 15 écrire x y échanger(x, y) . .
écrire x y échanger(x, y) entier x y lire x y 10 15 . . a º x entier b º y 15 entier . .
Dans echanger entier tmp entier tmp tmp ¬ a a ¬ b b ¬ tmp 10 15 . . a º x 10 entier b º y 15 entier entier tmp entier tmp tmp ¬ a a ¬ b b ¬ tmp tmp entier . .
Dans echanger entier tmp tmp ¬ a a ¬ b tmp ¬ a b ¬ tmp 10 15 10 . . a º x 10 entier b º y 15 entier entier tmp tmp ¬ a a ¬ b b ¬ tmp tmp 10 entier tmp ¬ a . .
Dans echanger entier tmp tmp ¬ a a ¬ b b ¬ tmp 15 15 10 . . a º x b º y 15 entier entier tmp tmp ¬ a a ¬ b b ¬ tmp tmp 10 entier . .
Dans la fonction echanger a º x 15 entier b º y 10 entier entier tmp tmp ¬ a a ¬ b b ¬ tmp tmp 10 entier . . b ¬ tmp
Retour à la fonction principale entier x y lire x y écrire x y échanger(x, y) Ce qui donne bien le résultat souhaité. a º x 15 entier b º y 10 entier tmp 10 entier . .
Résumé Les trois études de cas précédents ont servi à illustrer les concepts suivants: utilisation des fonctions pour étendre les possibilités de l'ordinateur. prototype d'une fonction définition d'une fonction. les paramètres l'appel d'une fonction le retour d'une fonction l'utilisation d'une variable tampon
{ { Définition des fonctions Dans la plupart des langage de programmation, la définition d'une fonction comporte deux parties: l'entête et le corps. { Nom de la fonction Type de la valeur retournée Nom des paramètres Types des paramètres Entête { Déclaration des variables Instructions Corps
Les paramètres Paramètres formels: Ceux utilisés dans la définition. Paramètres d'appel: Ceux utilisés lors de l'appel.
Les prototypes de fonctions Avant d'utiliser une fonction de la bibliothèque il faut connaître son prototype. Le prototype d'une fonction indique le nom de la fonction le type des paramètres le type de la valeur de retour Bien sûr, pour utiliser une fonction il n'est pas nécessaire de connaître le nom de ses paramètres formels.
Passage de paramètres Par copie: Les paramètres d'appel sont copiés dans les paramètres formels: création de nouvelles variables. Par référence: Les paramètre formels réfèrent aux paramètres d'appel: plusieurs noms pour une même case mémoire.
Appel de fonctions Création de nouvelles variables pour chacun des paramètres passés par copie. Le contrôle est donné à la fonction: la première ligne du corps de la fonction est d'abord exécutée. Remarques: Le nom des variables déclaré dans une fonctions est local à cette fonction et il est invisible aux autres fonctions. Deux fonctions distinctes peuvent utiliser le même identificateur pour nommer deux cases mémoire distinctes.
Retour d'une fonction À l'intérieur d'une fonction, l'instruction retourner expression est exécutée de la façon suivante: L'expression est d'abord évaluée. Le résultat est retourné à la fonction appelante Toutes les variables ayant été créées après l'appel de la fonction sont détruites. Le contrôle est redonné à la fonction appelante.
Version C entier nombre1 nombre2 nombre3 entier moyenne lire nombre1 nombre2 nombre3 moyenne ¬ moyenne3(nombre1, nombre2, nombre3) écrire moyenne #include <iostream.h> int moyenne3(int, int, int); /* prototype de la fonction*/ /* est déclaré avant la fonction main */ main() { int nombre1, nombre2, nombre3; int moyenne; cin >> nombre1 >> nombre2 >> nombre3; moyenne = moyenne3(nombre1, nombre2, nombre3); cout << moyenne; return (0) }
Version C Entête: entier moyenne3( entier a, entier b, entier c) Corps: entier m m¬ (a + b + c) / 3 retourner m int moyenne3(int a, int b, int c) /* définition de la fonction */ { int m; m = (a + b + c) / 3; return m; }
version C (air du rectangle) réel longueur largeur lire longueur largeur écrire aire(longueur, largeur) #include <iostream.h> double aire(double, double);/* prototype de la fonction*/ main() { double longueur, largeur; cin >> longueur >> largeur; cout << aire(longueur, largeur); /* appel de la fonction */ return(0) }
Version c Entête: réel aire(réel longueur, réel largeur) Corps: retourner longueur * largeur double aire(double longueur, double largeur) /* définition de la fonction */ { return (longueur * largeur); }
Version C (échanger) entier x y lire x y écrire x y échanger(x, y) #include <iostream.h> void echanger(int &, int &); main(){ int x, y; cin >> x >> y; cout << x << y; echanger(x, y); }
Version C Entête: échanger(entier référence a, entier référence b) Corps: entier tmp tmp ¬ a a ¬ b b ¬ tmp void echanger(int &a, int &b) { int tmp; tmp = a; a = b; b = tmp; }
void: est utilisé pour signifier: une fonction ne retourne pas de valeur; une fonction est définie sans paramètres
Remarque En C++, dans une fonction, le symbole & sert à indiquer qu'un paramètre est passé par référence. Il est utilisé dans l'entête de la définition ainsi que dans le prototype. Cependant, il n'est pas utilisé lors de l'appel de la fonction. En C il n`y a pas de passage de paramètres par référence. Pour obtenir le même effet (c'est-à-dire pour pouvoir changer la valeur d'un paramètre) il faut utiliser les pointeurs que nous verrons plus tard.
Autres exemples 1. En utilisant les fonctions, écrire un programme en C qui détermine le PGCD de deux valeurs.
#include <iostream.h> /* prototype dde la fonction*/ int PGCD(int,int); main(){ int m,n; cin >>n>>n; cout <<‘’le PGCD est ‘’<<PGCD(n.m); return (0); } */ définition de la fonction */ int PGCD(int A, int B){ int reste; reste = A % B; while (reste != 0){ A = B; B = reste; return (B); } /* fin de la fonction */
Exo 2. Reprendre l’exemple du calcul de la racine carrée avec une fonction #include <stdio> #include <math.h> double racine(double); */ prototype de la fonction */ main(){ double a, rac0,rac1; cin >>a; cout << "la racine carrée de "<<a; cout << " est "<<racine(a); return (0); } double racine(double a) { rac1 = (1+a)/2; do rac0 = rac1; rac1 = (rac0 + a/rac0)*0.5; while (fabs((rac1 – rac0)/rac0) > 0.00001) return (rac1);
3. À l’aide d’une fonction, écrire un programme qui visualise le texte: Bonjour, comment allez-vous? #include <iostream.h> void texte(void) /* prototype de la fonction*/ main(){ texte; /* appel à la fonction texte */ return (0); } void texte ( ) /* définition de la fonction */ { cout << ‘’Bonjour, comment allez-vous?\n’’;