• Aucun résultat trouvé

ANSI : Acronyme de American National Standards Institute, institution qui définit les normes d'un ensemble de systèmes et de

Les Fonctions Introduction

1 ANSI : Acronyme de American National Standards Institute, institution qui définit les normes d'un ensemble de systèmes et de

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________

Exemple :

Les paramètres NbArticles et PU dans la fonction Montant sont des paramètres de type données. C'est le également le cas des paramètres note1 et note2 dans la fonction Moyenne.

• Un paramètre "résultat" est une variable qui est destinée à contenir le résultat de l'action de la fonction. Sa valeur n'est pas significative avant le début.

Exemple:

int CalculerTriple( int x, int triple) {

triple = 3 *x; return triple; }

Dans cet exemple x est un paramètre "donnée" alors que triple est un paramètre "résultat". Généralement un paramètre "résultat" n'a pas besoin d'être déclaré comme argument de la fonction mais plutôt comme une variable locale.

• Un paramètre "donnée-résultat" est à la fois une donnée et un résultat. c'est à dire qu'il sert à passer une donnée à la fonction et que cette dernière modifie sa valeur.

Exemple :

Dans une fonction qui prend deux paramètres entiers et qui permute leurs valeurs. Ces entiers sont des paramètres "données-resultats" puisqu'ils fournissent les données en entrée (les valeurs à permuter) et stockent normalement le résultat de la fonction (les valeurs permutées).

Modes de passage des paramètres

Passage par valeur

• C'est le mode de passage d'arguments le plus courant en C/C++ et le plus simple également. Dans ce mode, les valeurs des arguments au moment de l'appel (paramètres effectifs) sont recopiées dans les éléments de données locaux correspondants à la fonction (paramètres formels). Après la sortie de la fonction, les valeurs des paramètres effectifs restent identiques à celles qu'ils avaient avant l'exécution de la fonction.

• Ce mode de passage est adapté aux paramètres de type "données". Pour les paramètres de type "résultat" ou "donnée-résultat", ce mode ne permet pas de récupérer les éventuels modifications effectuées en interne par la fonction à l'extérieur de cette dernière.

• La déclaration d'un passage par valeur s'effectue de la manière suivante :

TypeFonction NomFonction(……, Type ParamValeur,…); ParamValeur étant le paramètre passé par valeur et Type étant son type.

Exemple 1 :

Les deux notes sont passées à la fonction Moyenne par valeur.

Exemple 2 :

#include <stdio.h>

void CalculerTriple( int x, int triple) { triple = 3 *x; } void main(void) { int ent = 5; int res; CalculerTriple(ent,res);

printf("le résultat est %d", res); // donne un résultat incorrect }

Le mode de passage par valeur est adapté pour le paramètre x (paramètre "donnée") mais ne l'est pas pour le paramètre triple (paramètre "résultat"). En effet la modification effectuée sur ce paramètre à l'intérieur de la

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________ fonction CalculerTriple n'est pas récupérée à l'extérieur de cette dernière (dans le main dans ce cas de cet exemple).

Exemple 3 :

#include <stdio.h>

void Permuter (int val1,int val2) { int temp; temp =val1; val1= val2; val2= temp; } void main( ) { int x= 5, y=3; Permuter(x,y);

printf("x contient %d et y contient %d \n", x,y); // 5 et 3 }

Les paramètres val1 et val2 sont de type "donnée-résultat". En les faisant passer par valeur comme c’est le cas dans cet exemple, le résultat de la permutation n'est pas récupéré à l'extérieur de la fonction et le résultat affiché est par conséquent incorrect.

• Une des solutions permettant de remédier au problème du passage par valeur des paramètres "résultat" et "donnée-résultat" consiste à éliminer ces derniers de la liste des arguments de la fonction et à les déclarer comme des variables globales. Toutefois cette solution présente l'inconvénient de rendre la fonction difficilement réutilisable.

• Une deuxième solution plus intéressante consiste à utiliser un autre mode de passage de paramètres qui permet de récupérer ces modifications : c'est le cas du passage par adresse (C/C++) et du passage par référence (C++ seulement).

Passage par adresse

• Dans un passage par adresse d'un argument on impose à la fonction de travailler non plus sur une copie locale du paramètre effectif mais plutôt directement sur ce dernier. (Pas de copie du paramètre effectif dans le paramètre formel, le travail se fait directement sur le paramètre effectif). De cette façon, toute modification effectuée à l'intérieur de la fonction sera en réalité réalisée sur le paramètre effectif et par conséquent visible à l'extérieur de la fonction.

• Ce mode de passage de paramètre est effectué en faisant passé à la fonction l'adresse du paramètre effectif. Toute référence à ce dernier de l'intérieur de la fonction doit se faire à l'aide de l'opérateur d'indirection (*).

Déclaration d'un passage par adresse

TypeFonction NomFonction(……, Type* ParamAdresse,…); ParamAdresse étant le paramètre passé par adresse et Type étant son type.

Référence à l'intérieur de la fonction d'un paramètre passé par adresse

TypeFonction NomFonction(……, Type* ParamAdresse,…) { … … …

… *ParamAdresse; }

Passage du paramètre par adresse au moment de l'appel de la fonction

NomFonction(……, &ParamAdresse,…)

• Les paramètres concernés par le mode de passage par adresse sont ceux de type "résultat" ou "donnée- résultat". Les paramètres "donnée" peuvent toujours être passés par valeur.

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________

Exemple 1 :

#include <stdio.h>

void CalculerTriple(int x, int* triple) { *triple = 3*x; } void main(void) { int ent = 5; int res; CalculerTriple(ent, &res);

printf("le résultat est %d", res); // donne un résultat correct 15 }

Exemple 2 :

#include <stdio.h>

void Permuter (int* val1,int* val2) { int temp; temp =*val1; *val1= *val2; *val2= temp; } void main( ) { int x= 5, y=3; Permuter(&x,&y);

printf("x contient %d et y contient %d \n", x,y); // 3 et 5 }

Passage par référence (C++ seulement)

• Par rapport au C, le C++ introduit un nouveau mode de passage de paramètres appelé le passage par référence. Il est équivalent de point de vue effet au passage par variable du pascal ou au passage par adresse du C.

• Dans le passage par référence, toute modification effectuée sur le paramètre formel de la fonction est répercutée directement sur le paramètre effectif.

• La déclaration d'un passage par référence s'effectue en adjoignant au type du paramètre à passer le symbole & de la manière suivante :

Déclaration d'un passage par référence

TypeFonction NomFonction(……, Type& ParamRéférence,…); ParamRéférence étant le paramètre passé par référence et Type étant son type.

Référence à l'intérieur de la fonction d'un paramètre passé par référence

TypeFonction NomFonction(……, Type& ParamRéférence,…) { … … …

… ParamAdresse; }

Passage du paramètre par adresse au moment de l'appel de la fonction

NomFonction(……,ParamRéférence,…)

Exemple

#include <iostream.h>

void Permuter (int& val1,int& val2) { int temp;

temp =val1; val1= val2; val2= temp;

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________

void main( ) { int x= 5, y=3; Permuter(x,y);

cout<<" x contient : "<<x<<"\n y contient : "<<y<<endl; }

Transmission des tableaux comme arguments d'une fonction

Si un tableau figure comme argument d'une fonction alors son passage se fait toujours par adresse. Par conséquent, toutes les opérations et modifications appliquées à ses éléments seront visibles à l'extérieur de la fonction.

Exemple1:

Le programme suivant remplace les éléments d'un tableau par leurs valeurs absolues sans qu'il soit nécessaire de spécifier que le passage se fait par adresse.

#include <stdio.h>

void ValAbsTab( float[], int); void main( ) { float Tableau[5]; int i; for(i=0;i<5;i++) { printf("Donnez un nombre: "); scanf("%f", &Tableau[i]); } ValAbsTab(Tableau, 5); for(i=0;i<5;i++) printf("%f", Tableau[i]); }

void ValAbsTab( float TabVal[], int Taille) { int i; for(i=0;i<Taille;i++) if(TabVal<0) TabVal[ i ]= - TabVal[ i ]; } Remarque :

Les deux prototypes suivants sont également valables pour faire passer un tableau comme paramètre de la fonction ValAbsTab.

void ValAbsTab( float* TabVal, int Taille) void ValAbsTab( float TabVal[Taille], int Taille)

Exemple2: (cas d'un tableau à deux dimensions)

#include<stdio.h>

void SaisieMatrice( int[5][10], int, int); void main( )

{

int M[5][10];

SaisieMatrice(M,5,10); }

void SaisieMatrice( int Matrice[5][10], int L, int C) { int i,j; for(i=0;i<L;i++) for(j=0;j<C;j++) { printf("Donnez Matrice[%d][%d]",i,j); scanf("%d", &Matrice[i][j]); } }

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________

Remarque :

Les deux prototypes suivants sont également valables pour faire passer un tableau comme paramètre de la fonction SaisirMatrice.

void SaisieMatrice( int Matrice[ ][10], int L, int C) void SaisieMatrice( int** Matrice, int L, int C)

Les fonctions en ligne

• Une fonction en ligne est une fonction expansée à chaque appel. En d'autres mots, le corps de la fonction est inséré dans le module appelant à l'endroit de l'appel.

• Les fonctions en ligne permettent d'accélérer l'exécution (en évitant les allées-retours entre la fonction et le module appelant).

Déclaration :

inline Type NomFonction(Type1 arg1, Type2 arg2, … , TypeN argN);

Remarques :

• La définition d'une fonction en ligne doit obligatoirement précédée son appel. La déclaration seule ne suffit pas.

• inline est une recommandation au compilateur. Ce dernier peut l'ignorer si : o La fonction contient des boucles ou si elle est récursive.

o La fonction est trop grande pour que le gain en temps d'exécution soit significatif. • Les fonctions en ligne sont généralement de petite taille (1 à 3 instructions).

Exemple :

inline int abs(int x){return x>0?x:-x;} inline int max(int x, int y){return x>y?x:y;}

Surcharge de fonctions (surdéfinition)

• La surcharge de fonctions consiste à proposer plusieurs définitions d'une même fonction au sein d'un même programme. Ces définitions doivent se distinguer par leurs signatures.

• Il est à rappeler que la signature d'une fonction est définie par le nombre, le type et l'ordre de ses paramètres. La valeur de retour n'entre pas en considération dans ce cas.

• On surcharge généralement les fonctions qui font le même traitement mais sur des données de types différents.

Exemple 1:

///////////////////////////////////////////// int MaxTab(int* T, int n)

{ int i, max; for(i=0, max=T[0];i<n;i++) if(T[i]> max) max = T[i]; return max; } ///////////////////////////////////////////// double MaxTab(double* T, int n)

{ int i; double max; for(i=0, max=T[0];i<n;i++) if(T[i]> max) max = T[i]; return max; }

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________ ///////////////////////////////////////////// char MaxTab(char* T) { char max = T[0]; while(*T++) if(*T> max) max = *T; return max; } Exemple 2:

Arguments par défaut d'une fonction

Des valeurs par défaut peuvent être affectées aux arguments d'une fonction. Ces arguments ont généralement des valeurs habituelles qu'il n'est pas nécessaire de spécifier à chaque utilisation.

Déclaration

Type NomFonction(Type1 arg1, Type2 arg2, Type3 Arg3 = Val);

Exemple :

int f(int a, int b=0);

Remarque 1:

Seuls les derniers arguments, de la droite vers la gauche peuvent avoir des valeurs par défaut.

Exemple :

void g(int a = 3, int b, int c=5); // Error void g(int a, int b=6, int c =8); // OK

Remarque 2:

• Si la valeur d'un argument par défaut a été spécifiée dans la déclaration de la fonction, alors elle ne doit pas être mentionnée à nouveau dans la définition.

• Si une fonction est directement définie, alors la valeur par défaut doit être spécifiée dans la définition.

Exemple :

• Au moment de l'appel de la fonction, la spécification de l'argument par défaut est optionnelle.

Exemple :

// Déclaration

void Print(int Valeur, int Base = 10); // Appel

Print(31) ĺ 31 Print(31,10) ĺ 31

• Il est également possible de donner une nouvelle valeur pour l'argument par défaut.

Print(31,16) ĺ 1f Print(31,2) ĺ 11111

void f(int a = 0) // OK {Corps de la fonction} void f(int a=0); // ou void f(int=0);

void f(int a=0) // Error { Corps de la fonction }

// Surcharge incorrecte int f(char, int); char f(char, int); // Surcharge correcte

int f(int, char); int f(char, int);

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________

Les Pointeurs sur les fonctions

Les fonctions en C/C++ ne sont pas des entités variables. Néanmoins, il est possible de placer leurs noms dans des variables. En effet, le nom d'une fonction utilisé tout seul désigne l'adresse de l'emplacement mémoire où est chargé le code de la fonction (les instructions). Cette adresse peut être par conséquent stokée dans une variable de type pointeur. Elle peut également être passée comme paramètre à une autre fonction.

Déclaration d'un pointeur sur une fonction

La déclaration d'un pointeur sur une fonction se fait de la manière suivante : Type(*NomPointeur)(Type1 P1, Type2 P2, …,TypeN PN); • NomPointeur désigne le nom du pointeur à déclarer.

• Type désigne le type de la valeur retournée par la fonction pointée par le pointeur. • Type1,… TypeN désignent la liste des paramètres de la fonction pointée par le pointeur.

• Les parenthèses autour de (*pointeur) sont très importantes car autrement avec Type* pointeur(…) et en raison de la priorité de l'opérateur ( ) par rapport à * on déclarerait non plus un pointeur sur une fonction mais une fonction qui retourne une valeur de Type*.

Exemple :

int(*pf)(double,int);

Cette déclaration spécifie que pf est un pointeur qui peut pointer sur des fonctions prenant comme argument un double et un int et retournant un int.

Soit la fonction int f(double, int)

Si pf=f alors int k=f(5.4,6); ⇔ int k=(*pf)(5.4,6);

Exemple :

On veut écrire un programme qui affiche, suivant le choix de l'utilisateur le sinus, le cosinus ou la tangente d'un angle fourni par l'utilisateur.

#include <iostream.h> #include<math.h> void main( ) { double Deg, Rad; int NumF;

double(*Tpf[3])(double)={sin,cos,tan}; cout<<"Donnez un angle en degree : "; cin>>Deg;

cout<<"Tapez le numéro de la fonction que vous voulez utiliser\n"; cout<<" 1- sinus \n 2- cosinus\n 3- Tangente\n";

cin>>NumF;

Rad=3.1416*Deg/180;

cout<<"Le résultat est : "<<(*Tpf[NumF-1])(Rad)<<endl; }

Remarque : Différence entre le C et le C++ concernant les pointeurs sur des fonctions sans paramètres

• En langage C, la liste des paramètres spécifiée lors de la déclaration d'un pointeur sur une fonction n'est pas obligatoire. Son absence ne signifie pas forcément que la fonction référencée par le pointeur ne possède pas de paramètres. En effet, si rien n'est indiqué concernant les paramètres, le compilateur n'effectue aucune vérification en ce qui concerne la correspondance entre paramètres effectifs et paramètres formels (cette propriété existe déjà pour les fonctions). Ainsi la définition d'un pointeur de fonctions ayant une liste de paramètres vide peut présenter un avantage puisqu'un tel pointeur peut servir à mémoriser les adresses des fonctions admettant toutes sortes de paramètres.

Exemple :

int (*pf)();

pf=f1; ou pf = f2; avec f1 et f2 deux fonctions.

La seule contrainte ici pour que ces deux affectations soient correctes est que f1 et f2 retournent une valeur de type int. Les deux fonctions suivantes sont dans ce cas valables.

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________

• Contrairement au C, le compilateur C++ effectue toujours une vérification concernant la correspondance entre paramètres effectifs et paramètres formels même si ces derniers ne sont pas mentionnés. Par conséquent un pointeur de fonction déclarée sans paramètres ne peut pointer que sur une fonction n'ayant pas de paramètres. Le pointeur pf de l'exemple précédent ne peut pointer alors que sur f1.

Exemple :

L'exemple suivant est correct en C mais incorrect en C++.

int(*pf)() = printf;

printf("Test des pointeurs de fonctions");⇔(*pf)("Test des pointeurs de fonctions");

Passage d'une fonction comme argument d'une autre fonction

Du fait que le nom d'une fonction soit considéré comme une adresse, rien n'empêche de le passer en tant qu'argument à une autre fonction. Cette possibilité peut être utile dans certains cas même si elle reste rare d'utilisation.

Exemple :

On veut écrire deux fonctions qui font la saisie et l'affichage des éléments d'un tableau. Ces éléments peuvent être de type entier, réel ou caractère.

#include<stdio.h>

void SaisirChar(char* Tab, int l) { int i; for(i=0;i<l;i++) { scanf("%c", &Tab[i]); fflush(stdin); } }

void SaisirInt(int* Tab, int l) {

int i;

for(i=0;i<l;i++)

scanf("%d", &Tab[i]); }

void SaisirFloat(float* Tab, int l) {

int i;

for(i=0;i<l;i++)

scanf("%f", &Tab[i]); }

void AfficherChar(char* Tab, int l) {

int i;

for(i=0;i<l;i++)

printf("%c, ",Tab[i]); }

void AfficherInt(int* Tab, int l) { int i; for(i=0;i<l;i++) printf("%d, ",Tab[i]); } int f1() { /* intsructions*/ return 0; }

int f2(int a, int b, int c) {

/* intsructions*/ return C;

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________

void AfficherFloat(float* Tab, int l) {

int i;

for(i=0;i<l;i++)

printf("%f, ",Tab[i]); }

void AfficherTableau( void* Tab, int l, void(*pf)(void*, int)) {

(*pf)(Tab,l); }

void SaisirTableau( void* Tab, int l, void(*pf)(void*, int)) { (*pf)(Tab,l); } void main() { char C[5]; int I[5], l =5,n; float F[5];

printf("Tapez le numéro 1 du type des éléments que vous voulez saisir : \n 1 - caractère\n 2 - entier\n 3 - réel\n");

scanf("%d", &n); fflush(stdin);

printf("****** Début de la saisie : *******\n"); switch(n) { case 1: SaisirTableau(C,l,SaisirChar); break; case 2: SaisirTableau(I,l,SaisirInt); break; case 3: SaisirTableau(F,l,SaisirFloat); break; }

printf("****** Affichage du résultat de la saisie: *******\n"); switch(n) { case 1: AfficherTableau(C,l,AfficherChar); break; case 2: AfficherTableau(I,l, AfficherInt); break; case 3: AfficherTableau(F,l, AfficherFloat); break; } }

Fonctions avec un nombre variable de paramètres

• En programmation, il existe souvent des opérations pour lesquelles il n'est pas possible de préciser à l'avance le nombre de paramètres à traiter. L'utilisation des fonctions avec un nombre fixe de paramètres ne convient pas dans ce cas. Il faut plutôt passer par des fonctions possédant un nombre variable de paramètres.

• Les fonctions printf et scanf du C/C++ constituent deux exemples de ce genre de fonctions.

• Le concept de "fonction avec un nombre variable de paramètres" signifie que la fonction est définie avec un nombre quelconque de paramètres fixes (au moins un) et qu'elle accepte lorsqu'on l'appelle un nombre variables de paramètres effectifs supplémentaires sans qu'il y ait besoin que ces derniers soient explicitement définis à l'aide de paramètres formels.

Déclaration

La déclaration d'une fonction de la sorte se fait de la manière suivante : Type NomFonction(Liste des paramètres fixes, …);

Exemple :

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________ Evaluation des paramètres optionnels

• L'évaluation des paramètres effectifs optionnels doit passer par l'utilisation des trois macros suivantes va_start, va_arg et va_end du header stdarg.h ainsi que par un pointeur de type va_list (défini comme void* ou char*) et qui va permettre de pointer vers ces arguments optionnels (Un argument est un paramètre effectif).

La macro va_start

void va_start( va_list arg_ptr, prev_param ); (ANSI version) void va_start( va_list arg_ptr ); (UNIX version)

va_start prend deux paramètres, le premier est un pointeur de type va_liste vers les paramètres optionnels de la fonction alors que le second est le nom du dernier paramètre fixe. va_start initialise arg_ptr au premier argument optionnel de la liste des arguments passée à la fonction.

La macro va_arg

TypeArg va_arg( va_list arg_ptr, TypeArg );

La macro va_arg récupère une valeur de type TypeArg à partir de l'endroit donné par arg_ptr. En d'autres mots, elle permet de récupérer la valeur de l'argument courant. Elle incrémente aussi arg_ptr pour pointer sur l'argument suivant de la liste en utilisant la taille du type pour déterminer l'adresse de début du prochain argument.

La macro va_end

void va_end( va_list arg_ptr );

Après évaluation de la liste des paramètres, le pointeur d'arguments va_end réinitialise arg_ptr à NULL.

Remarque :

Dans la majorité des cas, Il est nécessaire de faire communiquer à la fonction acceptant un nombre variable de paramètres le nombre effectif de paramètres optionnels à traiter. Ce nombre est généralement placé dans un des paramètres fixes.

Exemple :

L'exemple suivant montre la définition d'une fonction Somme qui peut calculer la somme d'un nombre variable d'entiers. #include "iostream.h" #include "stdarg.h" long Somme(int L,...) { int i; va_list arg_ptr; long resultat; va_start(arg_ptr,L); i=1; resultat=0; while (i<=L) { resultat+=(long)va_arg(arg_ptr,int); i++; } va_end(arg_ptr); return resultat; } void main() {

cout<<"le résultat de la somme est :"<<f(3,3,5,10)<<endl; }

Les fonctions Programmation orientée objet (C++) _________________________________________________________________________________________________________________

Paramètres sur la ligne de commande

Considérons le programme suivant :

#include <stdio.h> void main()

{

int a,b,c;

printf("donnez trois entiers"); scanf("%d %d %d ",&a,&b,&c); printf("%d",a+b+c);

}

• Supposons que ce programme soit sauvegardé dans un fichier nommé Somme.cpp alors la compilation et l'édition des liens de ce fichier va conduire vers la création d'un fichier exécutable nommé Somme.exe. Pour lancer cet exécutable il suffit de taper son nom après le prompt du système d'exploitation puis de valider avec <entrée>. Le programme va demander alors à l'utilisateur de saisir trois nombres et lui affiche comme résultat leur somme.

• Il est également possible de faire fonctionner le programme Somme à la manière d'une commande qui prend trois arguments. Pour ce faire, ces arguments doivent être passés à la fonction principale main.

Passage d'arguments à la fonction main

La fonction main peut prendre normalement deux paramètres formels optionnels communément appelés argc et argv.

main(int argc, char* argv[])

• argc est un argument de type int et désigne le nombre de paramètres spécifiés sur la ligne de commande y compris le nom du programme qui est considéré aussi comme paramètre.

• argv est un tableau de chaînes de caractères qui stocke les arguments passés en ligne de commande. La taille de ce tableau dépend du nombre d'arguments passés. Par convention arg[0] renvoie sur la commande avec laquelle le programme est invoqué, arg[1] représente le premier argument passé réellement au programme, arg[2] représente l'argument suivant, etc. Le dernier élément de argv est toujours argv[argc] et contient le pointeur nul.

• Le premier argument passé est argv[1] et le dernier est argv[argc-1].

• La reconnaissance du nombre de paramètres est automatique. Ces derniers doivent cependant être séparés par des espaces. Si un des paramètres lui même est un espace alors il doit être placé entre deux guillemets.

Exemple :

#include <stdio.h> #include <stdlib.h>

void main(int argc, char* argv[]) { int a,b,c; a=atoi(argv[1]); b=atoi(argv[2]); c=atoi(argv[3]); printf("%d",a+b+c); }

Si on enregistre ce code dans un fichier portant le nom Somme.cpp alors l'exécution en ligne de commande devra se faire de la manière suivante :

C:\ Somme 4 5 2

C:\ 11

Remarque :

La fonction main peut prendre un troisième paramètre communément appelé envp. Ce paramètre pointe sur les rubriques de l'environnement du programme (les chemins implicites, l'allure du prompt,…) il est utilisé sous dos et unix.