Vous êtes sur la page 1sur 11

3ème Partie : Les fonctions

Définition
Une fonction est un groupe d’instructions qui exécutent ensemble une tâche. Chaque programme C
++ a au moins une fonction, qui est main, et tout programme peut définir des fonctions
supplémentaires.
Ainsi main est une fonction, et sa syntaxe pourra servir de modèle pour écrire de nouvelles fonctions.
Sin(x) est aussi une fonction, il faut inclure dans le programme la bibliothèque cmath pour qu’elle soit définie
L’utilisateur peut écrire ses propres fonctions comme la fonction polynôme par exemple.

Il ne faut pas confondre le sens du mot fonction en mathématique et son sens en C++. Son sens est plus large
en C++.Toutes les fonctions mathématiques connues sont groupées d’ores et déjà dans la bibliothèque
« cmath ». Mais bien plus, tout groupe d’instructions qui exécutent ensemble une tache peut former une
fonction. Une fonction est équivalente à un sous-programme, une procédure, un module ou une méthode d’un
point de vue lexical.
Diagramme schématique
De façon schématique, une fonction peut être représentée par une boite ou un mécanisme (constitué
d’un groupe d’instructions) qui produit un résultat à partir d'une ou plusieurs données.

(ENTRÉE) (SORTIE)

DONNÉES FONCTION RÉSULTAT

Arguments ou Valeur de
Paramètres Retour

A l’entrée de la fonction :

 Des données peuvent êtres requises ou non, cela dépend de la tâche à effectuer.
 Ces données sont appelées des arguments ou paramètres de la fonction.

A la sortie de la fonction :

 Un résultat unique peut être rendu ou non. Quand résultat il y a, il est appelé valeur de retour de
la fonction. Les fonctions qui n’ont pas de valeur de retour sont appelées des procédures.
 Les fonctions peuvent produire d’autres résultats en dehors de la valeur de retour par effet de
bord (side effects) ; c.-à-d. que pendant l’exécution de la fonction certaines données du
programme sont affectées directement par celle-ci. Les arguments de la fonction dans ce cas sont
de type pointeur ou référence. Une fonction peut également consigner des résultats dans des
fichiers ; ceux-ci peuvent être ouverts et consultés ailleurs dans le programme. Etc.

Avantage des foncions


Parmi les avantages offerts par l’usage des fonctions dans les programmes on cite :

 Une meilleure lisibilité.


 Une diminution du risque d'erreur.
 La possibilité de tests sélectifs.
 Une réutilisation de fonctions déjà existantes.
 La simplicité de l'entretien.
 La favorisation du travail en équipe.
 La hiérarchisation des modules.

Un exemple de fonction sur le modèle de main


Ecriture d’une fonction pour calculer les valeurs du polynôme pour les valeurs
0.5 et 1 de x.

La réponse est :
x: argument

int main()
{ double poly(double x)
double x=1; {
main cout << poly(0.5); double y; poly
cout << poly( x ); y = 2*x*x-10*x+3;
return 0; return y;
} }

y: valeur de
retour

Le programme sera composé de la fonction principale et de la fonction polynôme. On a représenté à


gauche et à droite les définitions des fonctions main et poly respectivement.

La fonction main contient les instructions : cout << poly(0.5); et cout << poly(x); qui
constituent des appels de la fonction poly, tout en donnant une valeur effective à l’argument : 0.5
pour le 1e appel et x (valant 1) pour le 2e.

La fonction poly prend comme argument un ‘double’ nommé x, dont elle se sert pour calculer y, un
‘double’ aussi. La valeur de retour est y.

La valeur retournée (-1.5 pour le 1e appel et -5 pour le 2e) sera affichée à l’instruction cout dans la
fonction main.

Il reste à mettre les deux fonctions dans un programme.

A la compilation tous les mots utilisés doivent être soit des mots-clefs, soit des mots définis dans des
bibliothèques comme iostream. Le mot poly n’est ni l’un ni l’autre. Il se trouve que sa définition est
l’ensemble d’instruction ci-dessus.

Pour que la fonction main ait connaissance de la définition du mot poly avant de l’utiliser, une des
façons de procéder serait de placer cette définition avant main comme suit :
#include<iostream>
#include<iomanip>

using namespace std;

double poly(double x)
{
double y;
y = 2*x*x-10*x+3;
return (y);
}

int main()
{
double x=1;
cout << poly(0.5);
cout << poly( x );
return 0;
}
Ainsi, dans le sens de la lecture, le programme aura connaissance de ce qu’est « poly » avant que ce
mot n’apparaisse dans « main » dans l’instruction :
cout << poly(0.5);
Cette instruction constitue « l’appel de la fonction » avec passation d’une valeur effective de
l’argument x.
L’expression poly(x) possède une valeur. Elle est le résultat de l’exécution des instructions de la
fonction poly jusqu’à l’instruction return. La valeur retournée est la valeur de la fonction.

Syntaxe des fonctions


Pour définir une fonction on a besoin d’un entête et d’un corps comme suit:
<type_return><nom_fonction>([<type><nom_arg_1>, <type><nom_arg_2>,,<type><nom_arg_n>])
{
corps de fonction
}
N.B. : les crochets […] présents dans cette définition signifient que les éléments contenus à l’intérieur sont
facultatifs et peuvent être omis. Ce n’est pas l’operateur [] du langage.

L’ensemble de ce texte constitue la définition de la fonction. Il permet d’identifier le sens du


mot nom_fonction dans la suite du programme.
type_return
Tout type prédéfini. Il correspond au type de la variable de
retour figurant dans l’instruction return. Si cette instruction est
absente ce type sera void.
nom_fonction
Nom donné à la fonction, on doit respecter les règles générales
des identificateurs.
()
Début et fin de la liste des arguments de la fonction.
type nom_arg_1, …
Liste des arguments ou paramètres de la fonction. C’est une
suite de noms de variables précédées de leurs types, comme dans
une instruction déclarative. On dira que ce sont des variables
d’entrée de la fonction.
Cette liste peut être vide de tout argument.
{}
Début et fin du corps de la fonction : ils délimitent le bloc
d’instructions qui composent la fonction.
Corps de fonction
Ensemble des instructions propres à la fonction. Une instruction
en particulier est spéciale, celle qui contient le mot-clef return :
return valeur_retour;
où valeur_retour est une quantité qui doit être définie dans la
fonction, et son type est type_return, celui qui est présent
devant nom_fonction.
La présence de l’instruction return est facultative.

Exemples de fonctions.
Exemple 1 : Foncions qui retourne le plus grand de deux nombre.
#include<iostream>
using namespace std;

int plusGrand(int a,int b)


{
if(a>b)
return a;
else
return b;
}

int main()
{
int a,b;
cout << "entrer a et b : ";
cin >> a >> b;
cout << "le plus grand est : " << plusGrand(a,b);
return 0;
}

Exemple 2 : Fonction qui dit si un nombre est premier ou non.


#include<iostream>
using namespace std;

bool estPremier(int a)
{
int compt, i;
for(i=1,compt=0;i<=a;i++)
if(a%i==0)
compt++;
if (compt>2) return false;
else return true;
}

int main()
{
int a;
cout << "entrer a : ";
cin >> a ;
if(estPremier(a))
cout << a << " est premier ";
else
cout << a << " n'est pas premier ";
return 0;
}

Exemple 3 : fonction qui affiche un simple message « Le ciel est bleu !»


#include<iostream>
using namespace std;

void cielBleu(void)
{
cout << "Le ciel est bleu !";
}

int main()
{
cielBleu();
return 0;
}

Positions relatives des fonctions dans les fichiers.


Soit un programme constituée de « main » et d’une fonction ; « plusGrand » par exemple.
#include<iostream> #include<iostream>
using namespace std; using namespace std;

int main() int plusGrand(int a,int b)


{ {
int a,b; int c = a;
cout << "entrer a et b : "; if( b>c ) c = b;
cin >> a >> b; return c;
cout << "le plus grand est : " << plusGrand(a,b); }
return 0;
}

Il est possible de placer les fonctions dans :

1. un même fichier.
a. avant la fonction principale main.
b. après elle.
2. des fichiers séparés.

Fonction dans un même fichier et avant la fonction main


Dans ce cas le programme complet s’écrira :
#include<iostream>
using namespace std;

int plusGrand(int a,int b)


{
int c = a;
if( b>c ) c = b;
return c;
}

int main()
{
int a,b;
cout << "entrer a et b : ";
cin >> a >> b;
cout << "le plus grand est : " << plusGrand(a,b);
return 0;
}

La définition de plusGrand est placée avant toute utilisation qui en sera faite dans la fonction
principale. La compilation se déroule normalement.
Fonction dans un même fichier mais après la fonction main
Si la fonction est placée après la fonction main, il faut faire une déclaration pour préciser le sens
du mot plusGrand pour main. Cette déclaration se fait au moyen d’une instruction, qui est l’entête de
la fonction :
int plusGrand(int a,int b) ;

Cette instruction (noter la présence de ;) est une déclaration de plusGrand, elle précise son type, ses
arguments et leurs types. Les noms des arguments a et b ne sont pas utile à ce stade. si bien que l’on
peut simplement écrire :
int plusGrand(int ,int ) ;

le programme aura alors de cette écriture :


#include<iostream>
using namespace std;

int main()
{
int plusGrand(int ,int ) ;
int a,b;
cout << "entrer a et b : ";
cin >> a >> b;
cout << "le plus grand est : " << plusGrand(a,b);
return 0;
}

int plusGrand(int a,int b)


{
int c = a;
if( b>c ) c = b;
return c;
}

Fonctions dans des fichiers séparés


Il est possible de partager un programme sur plusieurs fichiers, dans chaque fichier on pourrait mettre
une ou plusieurs fonctions. Cette méthode est très intéressante dès lors que le programme devient
trop volumineux, elle permet de travailler sur chaque partie séparément.

Pour appliquer cette méthode a l’exemple précèdent, on commence par créer deux fichiers séparés,
un pour main et l’autre pour la fonction. On leur donne les noms fichierPrincipal.cpp et
fichierFonction.cpp et on les place dans un même dossier.

Fichier de la fonction main Fichier de la fonction


#include<iostream> int plusGrand(int a,int b)
#include"fichierFonction.cpp" {
using namespace std; int c = a;
if( b>c ) c = b;
int main() return c;
{ }
int plusGrand(int ,int ) ;
int a,b;
cout << "entrer a et b : ";
cin >> a >> b;
cout << "le plus grand est : " << plusGrand(a,b);
return 0;
}
Nom : fichierPrincipal.cpp Nom : fichierFonction.cpp

Remarquez l’ajout dans main de la ligne :


#include"fichierFonction.cpp"
La fonction n’étant plus incluse dans le fichier de main, il faut maintenant l’importer par #include
comme c’est le cas pour cout avec iostream.

Notez aussi que l’operateur < > est remplacé par celui-ci " ".

Et si les 2 fichiers n’étaient pas dans le même dossier il faudrait mettre le chemin absolu jusqu’au
fichier de la fonction en commençant depuis la racine C ou D comme ceci :
#include"C:/dossier1/dossier2/…/fichierFonction.cpp"

Les arguments de fonctions


Les arguments permettent la communication entre la fonction et la fonction main (qui lui fait appel).
Par défaut les paramètres d'une fonction en C++ sont passés par valeur. Cela signifie
qu'à l'appel, les valeurs des paramètres effectifs sont copiées dans les arguments
correspondants.
En d’autres termes, les arguments communiquent leurs valeurs aux fonctions. Mais selon leurs types,
ces valeurs peuvent êtres des adresses. On va passer en revue les différents cas.
Passage par valeur
C’est le cas des arguments de types prédéfinis comme char, int, float etc.

Pour illustrer ce cas, on considère deux variables de type int ; et on écrit une fonction qui permet
d’échanger leurs valeurs. On l’appelle depuis main.
#include <iostream>
using namespace std ;

int main()
{
void echange(int a, int b) ;
int n=10, p=20 ;
cout << "\nDans Main (avant appel): " << n << '\t' << p << endl ;
echange(n, p) ;
cout << "\nDans Main (apres appel): " << n << '\t' << p << endl ;

return(0);
}

void echange(int a, int b)


{
int c ;
cout << "\nDans Fonction (avant): "<< a << '\t' << b << endl;
c = a, a = b, b = c;
cout << "\nDans Fonction (apres): "<< a << '\t' << b << endl;
}
Dans Main (avant appel): 10 20
Dans Fonction (avant): 10 20
Dans Fonction (apres): 20 10
Dans Main (apres appel): 10 20
La fonction « echange » reçoit deux valeurs correspondant à ses deux arguments muets a et b.
Elle effectue un échange de ces deux valeurs. Mais, lorsqu’on est revenu dans le programme principal,
aucune trace de cet échange ne subsiste sur les paramètres effectifs n et p.
En effet, lors de l’appel de « echange » il y a eu transmission de la valeur des expressions n et p.
On peut dire que ces valeurs ont été recopiées localement dans la fonction « echange » dans des
emplacements nommés a et b. C’est effectivement sur ces copies qu’a travaillé la fonction « echange »,
de sorte que les valeurs des variables n et p n’ont, quant à elles, pas été modifiées.
C’est ce qui explique le résultat constaté.
On peut dire Les variables n et p sont simplement des paramètres effectifs en lecture seule.

Passage par référence


En reprenant le même exemple que ci-dessus, mais en prenant des références pour argument de la
fonction échange, les résultats changent.

#include <iostream>
using namespace std ;

int main()
{
void echange(int &a, int &b) ;
int n=10, p=20 ;
cout << "\nDans Main (avant appel): " << n << '\t' << p << endl ;
echange(n, p) ;
cout << "\nDans Main (apres appel): " << n << '\t' << p << endl ;

return(0);
}

void echange(int &a, int &b)


{
int c ;
cout << "\nDans Fonction (avant): "<< a << '\t' << b << endl;
c = a, a = b, b = c;
cout << "\nDans Fonction (apres): "<< a << '\t' << b << endl;
}

Dans Main (avant appel): 10 20


Dans Fonction (avant): 10 20
Dans Fonction (apres): 20 10
Dans Main (apres appel): 20 10

Comparons la ligne d’appel avec la déclaration de la fonction.


Ligne d’appel : Déclaration de la fonction :
echange(n, p); void echange(int &a, int &b) ;

Tout se passe comme si on écrivait :


int &a = n;
int &b = p;
ce qui transforme a et b en référence sur n et p. Donc tout écriture sur les argument a et b est une
écriture sur n et p.
Par comparaison avec le cas précèdent, les arguments sont en lecture et en écriture et non plus en
lecture simple.
Ainsi, toute modification de l’argument dans la fonction se répercute directement sur le paramètre
effectif correspondant dans main. Tout se passe comme si la fonction agissait directement sur le
paramètre effectif.
Dans notre exemple, les deux arguments a et b ont le statut donnée - résultat ou entrée - sortie.
Pour que l'échange se répercute sur les paramètres effectifs n et p, il faut donc passer a et b par
référence.
Remarque:
L’usage des références comme argument élimine les appels de fonction avec des valeurs constantes
(chose qui était possible avec les passages par valeurs)
Ligne d’appel : Déclaration de la fonction :
echange(10, 20); void echange(int &a, int &b) ;
Donnerait une erreur de compilation.
Passage par pointeurs
En restant sur le même exemple de l’échange de valeur entre deux variables a et b. Mais en prenant des
pointeurs pour argument de la fonction, le résultat est similaire à celui des passages par références.
#include <iostream>
using namespace std ;

int main()
{
void echange(int *a, int *b) ;
int n=10, p=20 ;
cout << "\nDans Main (avant appel): " << n << '\t' << p << endl ;
echange(&n, &p) ;
cout << "\nDans Main (apres appel): " << n << '\t' << p << endl ;

return(0);
}

void echange(int *a, int *b)


{
int c ;
cout << "\nDans Fonction (avant): "<< *a << '\t' << *b << endl;
c = *a, *a = *b, *b = c;
cout << "\nDans Fonction (apres): "<< *a << '\t' << *b << endl;
}

Dans Main (avant appel): 10 20


Dans Fonction (avant): 10 20
Dans Fonction (apres): 20 10
Dans Main (apres appel): 20 10

Comparons la ligne d’appel avec la déclaration de la fonction


Ligne d’appel : Déclaration de la fonction :
echange(&n, &p); void echange(int *a, int *b) ;
Pour chaque argument on aurait :
int *a = &n;
int *b = &p;
D’où a serait un pointeur sur n et b un pointeur sur p. et par conséquent toute modification sur a ou
b affecterait directement les états de n et p.
Les arguments de la fonction sont donc des données - résultats.
Conclusion
Nous avons évoqué tout au début de ce chapitre qu’une instruction return ne pouvait retourner
qu’une seule valeur. Avec le passage les arguments par référence, ou par pointeurs on peut faire en
sorte que plusieurs valeurs soient retournées par effets-de-bord (side-effects).
Par exemple, une fonction qui calcule les données d’une sphère : périmètre de son équateur, surface
et volume à partir de son rayon.
#include<iostream>
#include<cmath>
using namespace std;

void dataSphere( double R, double &E, double &S, double &V )


{
const double pi= acos(-1);
E = 2*pi*R;
S = 4*pi*R*R;
V = 4*pi*R*R*R/3;
}

main ( )
{
double rayon, equateur, surface, volume;
cout << " rayon = " ;
cin >> rayon ;
dataSphere ( rayon, equateur, surface, volume);
cout << " equateur = " << equateur << endl ;
cout << " surface = " << surface << endl ;
cout << " volume = " << volume << endl ;
}
rayon = 1
equateur = 6.28319
surface = 12.5664
volume = 4.18879

Le cas des tableaux.


Le moyen de transmission des tableaux en tant que paramètres de fonction est le passage par adresse.
En effet, un tableau déclaré dans cette instruction :
<type> tab ;
Fait que tab est un pointeur sur le premier élément du tableau. Sa transmission comme argument
effectif nécessite que l’argument muet soit un pointeur de type de tab. Examinons la correspondance
appel-fonction :

Ligne d’appel : Déclaration de la fonction :


fonction(tab); <Type> fonction(<type de tab> *t) ;
Puisque l’information passée concerne le 1er élément du tableau, il faut aussi communiquer la taille du
tableau sous forme d’un entier.

Dans cet exemple on considère un tableau dont il faut calculer la valeur moyenne et remettre à zéro
ses éléments tout en affichant les résultats.
#include<iostream>
using namespace std;

int main(void)
{
void affichage(float *t,int n);
void raz(float *t, int n);
float moyenTab(float *t, int n);

int n=5;
float tab[5] = {4.0, 1.6, 2.5, 3.6, 4.9};

cout << "\nTableau etat initial :\n";


affichage(tab, n);

/** Calcul de la moynne **/


cout << "\nMoyenne du tableau : " << moyenTab(tab, n);

/** remise azero de tab **/


raz(tab, n);
cout << "\ntableau apres raz : \n" ;
affichage(tab, n);

return 0;
}

void affichage(float *t,int n)


{
for(int i=0; i<n; i++)
cout << t[i] << "\t";
}

void raz(float *t, int n)


{
for(int i=0; i<n; i++)
*(t+i) = 0;
}

float moyenTab(float *t, int n)


{
float moy=0;
for(int i=0; i<n; i++) moy += t[i]/n;
return moy;
}
Tableau etat initial :
4 1.6 2.5 3.6 4.9
Moyenne du tableau : 3.32
tableau apres raz :
0 0 0 0 0

Vous aimerez peut-être aussi