CINI – Li115 1 Semaine 6 Les fonctions ● Pourquoi écrire des fonctions ? ● Définition de fonctions ● Fonction main ● Appels de fonctions ● Portée des variables ● Passage par valeur
CINI – Li115 2 Pourquoi écrire des fonctions ? (1) ● Pourquoi écrire des fonctions ? ● Jusqu'à présent, nous avons écrit de petits programmes permettant de résoudre de petits problèmes. ● Pour de plus grands problèmes, il est nécessaire de les décomposer en sous-problèmes ● → écriture de fonctions ● Qu'est-ce qu'une fonction ? ● Une fonction définit le programme permettant de résoudre un problème. ● Ce problème peut lui-même être décomposé.
CINI – Li115 3 Pourquoi écrire des fonctions ? (2) ● Les fonctions constituent le mécanisme principal de structuration offert par le langage C. ● Avantages des fonctions : ● Meilleure organisation et lisibilité du code : – Le programme est décomposé en sous-programmes. ● Plus facile à « débugger » : – Chaque fonction peut être testée séparément du reste du programme. ● Ré-utilisabilité du code : – Il est possible de réutiliser des fonctions déjà écrites pour résoudre d'autres problèmes. C'est le cas des bibliothèques de fonctions prédéfinies. ● Mutualisation du code : – Possibilité d'échanger des fonctions avec d'autres programmeurs et d'utiliser leur code.
CINI – Li115 4 Pourquoi écrire des fonctions ? (3) ● Exemples : #include int main(){ /* affichage de factorielle 5 et factorielle 9 */ int fact5, fact9; int i; fact5 = 1; fact9 = 1; for(i=1; i<=5; i++){ fact5 = fact5 * i; } CINI_print_string("La factorielle de 5 vaut : "); CINI_print_int(fact5); CINI_newline(); for(i=1; i<=9; i++){ fact9 = fact9 * i; } CINI_print_string("La factorielle de 9 vaut : "); CINI_print_int(fact9); CINI_newline(); return 0; }
CINI – Li115 5 Pourquoi écrire des fonctions ? (4) ● Exemples : #include int factorielleN(int n){ int res = 1; int i; for(i=1; i<=n; i++){ res = res * i; } return res; } int main(){ /* affichage de factorielle 5 et factorielle 9 */ int fact5, fact9; fact5 = factorielleN(5); fact9 = factorielleN(9); CINI_print_string("La factorielle de 5 vaut : "); CINI_print_int(fact5); CINI_newline(); CINI_print_string("La factorielle de 9 vaut : "); CINI_print_int(fact9); CINI_newline(); return 0; }
CINI – Li115 6 Pourquoi écrire des fonctions ? (5) ● Exemples : #include int factorielleN(int n){ int res = 1; int i; for(i=1; i<=n; i++){ res = res * i; } return res; } void afficheFact(int n, int factN){ CINI_print_string("La factorielle de "); CINI_print_int(n); CINI_print_string(" vaut : "); CINI_print_int(factN); CINI_newline(); } int main(){ /* affichage de factorielle 5 et factorielle 9 */ afficheFact(5,factorielleN(5)); afficheFact(9,factorielleN(9)); return 0; }
CINI – Li115 7 Définition de fonctions (1) ● Principe d'une fonction : ● 0 à n 0 à 1 valeur ● paramètres de retour ● Définition d'une fonction Corps de la fonction : bloc d'instructions En-tête de la fonction type_retour identifiant (paramètres_formels) { Suite d'instructions }
CINI – Li115 8 Définition de fonctions (2) ● Exemples : int somme (int a, int b){ int res = a + b; return res; } int sommeBis(int a, int b){ return a + b; } float multiplie (float f, int a){ return f * a ; } void afficheCoucou(){ CINI_print_string("Coucou"); CINI_newline(); }
CINI – Li115 9 Définition de fonctions (3) ● Type de retour : type de la valeur renvoyée par la fonction ● → int, float, bool, char, void ● Une fonction dont le type de retour est « void » est une fonction qui ne retourne rien. Ces fonctions sont également appelées « procédures » dans d'autres langages. ● Identifiant : mêmes règles que pour les noms de variables ● Paramètres formels : ● Liste de déclarations des récipients (type et nom) associés aux arguments transmis à la fonction lors de son appel. ● Cette liste peut être vide. ● Le type de chaque variable doit être spécifié. ● Les déclarations des variables sont séparées par des virgules.
CINI – Li Définition de fonctions (4) ● Corps de la fonction : suite d'instructions Le corps de la fonction définit la suite des instructions nécessaires à la réalisation du sous-programme. ● Le corps de la fonction est un bloc d'instructions. ● Les déclarations de variables sont réalisées en tête du bloc de la fonction. ● Pour toute exécution de fonction dont le type de retour n'est pas void, chaque branche d'exécution doit se terminer par une instruction de retour de valeur : – return expression ; ● Où expression correspond au résultat retourné par la fonction. ● Le type de l'expression est le même que le type de retour de la fonction.
CINI – Li Définition de fonctions (5) ● Que se passe-t-il si l'instruction return d'une fonction de type de retour non void est « oubliée » ? int somme (int a, int b){ int res = a + b; } Cours6ex0.c:5: attention : « return » manquant dans une fonction devant retourner une valeur Attention à la compilation :
CINI – Li Définition de fonctions (6) ● Prototype d'une fonction : type_retour identifiant (paramètres_formels); ● Le prototype d'une fonction décrit comment utiliser cette fonction. ● Le prototype est une déclaration. Il déclare la fonction. ● Le prototype n'est pas suivi du corps de la fonction. ● Le prototype est également appelé : signature de fonction, en- tête de fonction ou déclaration de fonction. ● Exemples de prototypes : int factorielleN(int n); ● int somme(int a, int b); void afficheCube(int n);
CINI – Li Définition de fonctions (7) ● Conseils pour écrire des fonctions : ● Ecrire le plus possible de fonctions, les plus petites possibles (réutilisation future). ● Toujours regarder s'il n'existe pas une fonction dans une bibliothèque qui résout le problème. ● Toujours commencer par se demander quels sont les paramètres formels et le type de retour de la fonction. ● Différencier les fonctions qui réalisent des affichages et les fonctions qui retournent des résultats. ● Mettre un commentaire au dessus pour expliquer ce que fait la fonction et comment elle s'utilise !
CINI – Li Fonction main ● La fonction de nom « main » ● ne déclare aucun paramètre formel (pour l'UE LI115), ● et retourne un entier. ● L'entier 0 indique que le programme s'est terminé sans erreur (convention du langage C). ● La fonction de nom « main » est la fonction principale du programme. Elle est appelée automatiquement lorsque le programme est exécuté.
CINI – Li Appels de fonctions (1) ● La définition d'une fonction n'exécute pas le programme (suite d'instructions) qui la compose. ● Pour exécuter les instructions, il faut appeler la fonction : – identifiant (paramètres_effectifs); ● Paramètres formels, paramètres effectifs : ● Les paramètres formels sont utilisés lors de la définition de la fonction. ● Lors de l'appel, des valeurs doivent être données à ces paramètres → paramètres effectifs. ● Un paramètre effectif est soit une variable (sa valeur), soit une valeur d'expression ou de retour d'un appel de fonction.
CINI – Li Appels de fonctions (2) ● Exemple : #include ● int cube(int x){ ● return x * x * x; ● } ● void afficheCube(int x){ ● int cube = x * x * x; ● CINI_print_int(cube); ● } ● int main(){ ● int res = cube(3); ● CINI_print_int(res); ● CINI_newline(); ● afficheCube(4); ● CINI_newline(); ● return 0; ● } => L'exécution affiche Paramètres effectifs Paramètres formels
CINI – Li Appels de fonctions (3) ● Attention : ● La définition d'une fonction doit toujours précéder son appel. ● L'appel d'une fonction peut se faire uniquement à l'intérieur d'une autre fonction. ● La seule fonction qu'il n'y a pas besoin d'appeler explicitement est la fonction main. ● Le nombre et l'ordre des paramètres formels et des paramètres effectifs d'une fonction doivent être identiques. ● Les variables utilisées au sein d'une fonction doivent être déclarées comme variables locales en tête du corps de la fonction, ou doivent correspondre à des paramètres de la fonction.
CINI – Li Appels de fonctions (4) ● Exemple : Cours6ex1.c: In function ‘main’: Cours6ex1.c:12: attention : implicit declaration of function ‘afficheCube’ 1#include 2 3int cube(int x){ 4 return x * x * x; 5} 6 7 8int main(){ 9 int res = cube(3); 10 CINI_print_int(res); 11 CINI_newline(); 12 afficheCube(4); 13 CINI_newline(); 14 return 0; 15} 16 17void afficheCube(int x){ 18 int cube = x * x * x; 19 CINI_print_int(cube); 20} Attention à la compilation :
CINI – Li Appels de fonctions (5) ● Exemple : 1#include ● 2 ● 3int cube(int x){ ● 4 return x * x * x; ● 5} ● 6 ● 7void afficheCube(int x){ ● 8 int cube = x * x * x; ● 9 CINI_print_int(cube); ● 10} ● 11 ● 12afficheCube(4); ● 13 ● 14int main(){ ● 15 int res = cube(3); ● 16 CINI_print_int(res); ● 17 CINI_newline(); ● 18 return 0; ● 19 } Cours6ex2.c:12: erreur: expected declaration specifiers or ‘...’ before numeric constant Cours6ex2.c:12: attention : data definition has no type or storage class Cours6ex2.c:12: attention : type defaults to ‘int’ in declaration of ‘afficheCube’ Cours6ex2.c:12: erreur: conflicting types for ‘afficheCube’ Cours6ex2.c:7: erreur: previous definition of ‘afficheCube’ was here Erreurs de compilation :
CINI – Li Appels de fonctions (6) ● Exemple : Cours6ex3.c: In function ‘main’: Cours6ex3.c:14: erreur: too many arguments to function ‘cube’ 1#include 2 3int cube(int x){ 4 return x * x * x; 5} 6 7void afficheCube(int x){ 8 int cube = x * x * x; 9 CINI_print_int(cube); 10} int main(){ 14 int res = cube(3, 5); 15 CINI_print_int(res); 16 CINI_newline(); 17 afficheCube(4); 18 CINI_newline(); 19 return 0; 20 } Erreurs de compilation :
CINI – Li Appels de fonctions (7) ● Exemple : 1#include 2 3int cube(int x){ 4 return a * a * a; 5} 6 7void afficheCube(int x){ 8 int cube = x * x * x; 9 CINI_print_int(cube); 10} int main(){ 14 int res = cube(3); 15 CINI_print_int(res); 16 CINI_newline(); 17 afficheCube(4); 18 CINI_newline(); 19 return 0; 20 } Cours6ex4.c: In function ‘cube’: Cours6ex4.c:4: erreur: ‘a’ undeclared (first use in this function) Erreurs de compilation :
CINI – Li Portée des variables (1) ● Variables locales / variables globales : ● Une variable déclarée à l'intérieur d'une fonction est une variable locale. ● Une variable déclarée en début de programme (après les #include et les #define) est une variable globale. ● Portée des variables : ● La portée d'une variable désigne la partie du programme dans laquelle on peut l'utiliser. ● Une variable locale n'est utilisable qu'à l'intérieur de la fonction dans laquelle elle est déclarée. ● Une variable globale est utilisable dans tout le programme. ● Les paramètres formels d'une fonction correspondent à des variables locales initialisées lors de l'appel de la fonction.
CINI – Li Portée des variables (2) ● Structure d'un programme : #include #define... /* Déclaration des variables globales */ /* Définition des fonctions */ int main(){ /* Définition des éventuelles variables locales de main */... return 0; }
CINI – Li Portée des variables (3) ● Exemple : #include int a = 2; int somme(int y){ int x = 3; return a + x + y; } int main(){ int res = somme(7); CINI_print_int(re s); CINI_newline(); return 0; } Portée de la variable globale a Portée de la variable locale x Portée de la variable locale res Portée de la variable locale y
CINI – Li Portée des variables (4) ● Que se passe-t-il en mémoire ? #include int a = 2; int somme(int y){ int x = 3; return a + x + y; } int main(){ int res = somme(7); CINI_print_int(re s); CINI_newline(); return 0; } Avant l'exécution de main : Empilement de la variable locale de main : Après l'appel à somme: Empilement du paramètre d'appel et de la variable locale de somme :
CINI – Li Portée des variables (5) ● Attention : ● Dans une fonction, les paramètres formels et les variables locales déclarées explicitement doivent porter des noms différents. #include int a = 2; int somme(int y){ int y = 3; return a + y + y; } int main(){ int res = somme(7); CINI_print_int(res); CINI_newline(); return 0; } Erreur de compilation : Cours6ex5.c: In function ‘somme’: Cours6ex5.c:7: erreur: ‘y’ redeclared as different kind of symbol Cours6ex5.c:6: erreur: previous definition of ‘y’ was here
CINI – Li Portée des variables (6) ● Variables locales et globales de même nom : ● Lorsqu'une variable locale et une variable globale portent le même nom, la portée de la variable locale masque la portée de la variable globale. #include int a = 2; int somme(int y){ int x = 3; int a = 5; return a + x + y; } int main(){ int res = somme(7); CINI_print_int(re s); CINI_newline(); CINI_print_int(a) ; CINI_newline(); return 0; } Affichage : 1 5 2
CINI – Li Portée des fonctions (1) ● Une fonction f peut être appelée dans n'importe quelle fonction définie après la définition de f. ● Une fonction ne peut pas être appelée en dehors de toute fonction. #include int f1(int y){ int a = 5; return a + x ; } int f2(int x){ return x*x; } int f3(int x){ return x* f2(x); } int main(){ int res = f1(7); CINI_print_int(f2(res )); CINI_newline(); CINI_print_int(f3(res )); CINI_newline(); return 0; } On ne peut PAS appeler f2 et f3 On peut appeler f1 On ne peut PAS appeler f3 On peut appeler f1 et f2 On peut appeler f1, f2 et f3
CINI – Li Portée des fonctions (2) ● Une fonction f1 peut être définie localement à une autre fonction f2. ● La définition de f2 doit être incluse dans f1. ● f2 n'est utilisable que dans f1. #include int f1(int x){ int f2(int x){ return x*x; } return x* f2(x); } int main(){ int res = f1(7); CINI_print_int(res); CINI_newline(); return 0; } Portée de f2 On peut appeler f1 On ne peut PAS appeler f2 A éviter absolument !
CINI – Li Passage par valeur (1) ● Que se passe-t-il ? #include void mystere(int y){ y = 3; } int main(){ int y = 7; CINI_print_int(y); CINI_newline(); mystere(y); CINI_print_int(y); CINI_newline(); return 0; } Quelles sont les valeurs affichées ?
CINI – Li Passage par valeur (2) ● Que se passe-t-il ? #include void mystere(int y){ y = 3; } int main(){ int y = 7; CINI_print_int(y); CINI_newline(); mystere(y); CINI_print_int(y); CINI_newline(); return 0; } Avant mystere :Entrée dans mystere : Avant de sortir de mystere : Après mystere : → y de main → y de mystere → y de main
CINI – Li Passage par valeur (3) ● En C, les paramètres d'une fonction sont passés par valeur : #include void mystere(int y){ y = 3; } int main(){ int y = 7; CINI_print_int(y); CINI_newline(); mystere(y); CINI_print_int(y); CINI_newline(); return 0; } On travaille sur la variable locale y de la fonction mystere et non sur la variable locale y de main. Seule la valeur de la variable y de main est passée en paramètre. => à la sortie de la fonction mystere, la variable locale y de main est inchangée
CINI – Li Passage par valeur (4) ● Comment, via l'exécution de la fonction mystere, modifier la valeur de y qui est affichée ? #include int mystere(){ return 3; } int main(){ int y = 7; CINI_print_int(y); CINI_newline(); y = mystere(); CINI_print_int(y); CINI_newline(); return 0; } En retournant une valeur : OU En déclarant y comme une variable globale : #include int y; void mystere(){ y = 3; } int main(){ y = 7; CINI_print_int(y); CINI_newline(); mystere(); CINI_print_int(y); CINI_newline(); return 0; }
CINI – Li Passage par valeur (5) ● En résumé, une fonction : ● Affiche un résultat (chaîne de caractères ou valeur d'une variable) ● OU ● Modifie une variable globale ● OU ● Retourne un résultat ● En LI115, ces « OU » seront exclusifs afin de mettre en oeuvre de bonnes pratiques de programmation. ● Nous ne définirons donc pas de fonctions qui affichent ET retournent un résultat.