La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

1 Convention sur les acétates Concernant la présentation des acétates : –malgré le fait quelles soient relativement bien remplies, les acétates tentent.

Présentations similaires


Présentation au sujet: "1 Convention sur les acétates Concernant la présentation des acétates : –malgré le fait quelles soient relativement bien remplies, les acétates tentent."— Transcription de la présentation:

1

2 1 Convention sur les acétates Concernant la présentation des acétates : –malgré le fait quelles soient relativement bien remplies, les acétates tentent de présenter un contenu : cohérent facile à suivre avec un contenu pouvant servir de référence Concernant les exemples : –plusieurs exemples sont inclus afin dillustrer les différents concepts enseignés –certains exemples contiennent quelques notions plus avancés à titre de référence –afin dillustrer davantage les notions du langage C, peu dexemple sont en pseudo code

3 2 Convention sur les acétates Coloration du texte en fonction des éléments du langage C Mots réservés en bleuConstantes en rouge Instructions en mauveCommentaires en vert Format des exemples Stéréotypes de : déclarations, fonctions, éléments de langage,... Exemples de : déclarations, fonctions, éléments de langage, portions de code, programmes,... Exemple de pseudo code pour représenter des : algorithmes, éléments logiques,...

4 3 Généralité du langage C Importance de la programmation –Que permet la programmation? –Outil technologique incomparable –Donne une longueur davance à ceux qui la connaisse –Exemples concrets dapplication Informatique « utilitaire » –Automatisation de tâches routinières () Informatique de gestion –Gestion de projet gestion des dépassements de coût, gestion des feuilles de temps,... –Gestion des ressources – gestion du matériel, gestion du personnel,... –Gestion de linformation – sauvegarde dinformation, base de connaissance,... Informatique industriel –Contrôle de procédé contrôle industriel, optimisation de systèmes de production,... –Manipulation de données acquisition, interprétation, prise de décision, représentation,... robotique industriel, représentation graphique, vision, réseaux de neurones, ***

5 4 Généralité du langage C Historique –1960 – Algol60 très abstrait, donne le Pascal, PL/I et CPL –1967 – BCPL par Martin Richards Basic Combined Programming Language –1970 – Langage B par Ken Thompson afin dassurer lévolution de Unix écrit en assembleur, son créateur crée ce langage inspiré du BCPL –1972 – Langage C par Dennis Ritchie et Ken Thompson après modification du langage B

6 5 Généralité du langage C Près du langage machine –initialement destiné à la programmation de système –assembleur évolué Langage typé faible –permet laffectation de types différents –contrairement au langage C++

7 6 Généralité du langage C Constituants dun programme –Essentiel Fonction main Utilisation dinstructions de contrôle, de branchement et ditération Utilisation dinstructions daccès mémoire, darithmétique et de logique –Possible Plusieurs fichiers sources référencés par #include Déclaration, définition et utilisation de fonctions personnalisées Appel de fonctions provenant des –déclarations personnalisées –bibliothèques standard

8 7 Généralité du langage C Exemple dun programme très simple #include "stdio.h" /* début du programme */ void main(void) { printf("Ceci est un programme simple!"); return; } Ceci est un programme simple! - aucune entrée - Entrée Sortie

9 8 Généralité du langage C Exemple dun programme #include "stdio.h" /* début du programme */ void main(void) { char C; do { C = getch(); printf("%c - %d - %x\n", C, C, C); } while (C != EOF); return; } G P A , c J a ' a d o f r e G P A , c J a ' a d o f r e GPA665, Jadore Entrée Sortie

10 9 Généralité du langage C Exemple dun programme #include "stdio.h" /* début du programme */ void main(void) { int i, n, Sum; float Mean; n = 15; Sum = 0; Mean = 0.0f; for (i = 0; i < n; i++) { Sum += i; printf("Somme cumulative a l'element %02d : %03d\n", i, Sum); } Mean = (float) Sum / n; printf("\nMoyenne des %d element(s) : %0.2f", n, Mean); return; } Somme cumulative a l'element 00 : 000 Somme cumulative a l'element 01 : 001 Somme cumulative a l'element 02 : 003 Somme cumulative a l'element 03 : 006 Somme cumulative a l'element 04 : 010 Somme cumulative a l'element 05 : 015 Somme cumulative a l'element 06 : 021 Somme cumulative a l'element 07 : 028 Somme cumulative a l'element 08 : 036 Somme cumulative a l'element 09 : 045 Somme cumulative a l'element 10 : 055 Somme cumulative a l'element 11 : 066 Somme cumulative a l'element 12 : 078 Somme cumulative a l'element 13 : 091 Somme cumulative a l'element 14 : 105 Moyenne des 15 element(s) : 7.00 Somme cumulative a l'element 00 : 000 Somme cumulative a l'element 01 : 001 Somme cumulative a l'element 02 : 003 Somme cumulative a l'element 03 : 006 Somme cumulative a l'element 04 : 010 Somme cumulative a l'element 05 : 015 Somme cumulative a l'element 06 : 021 Somme cumulative a l'element 07 : 028 Somme cumulative a l'element 08 : 036 Somme cumulative a l'element 09 : 045 Somme cumulative a l'element 10 : 055 Somme cumulative a l'element 11 : 066 Somme cumulative a l'element 12 : 078 Somme cumulative a l'element 13 : 091 Somme cumulative a l'element 14 : 105 Moyenne des 15 element(s) : aucune entrée - Entrée Sortie

12 11 Types de base 5 types de base –char, int, float, double et void 2 modificateurs de type –Signe (pour les entiers seulement char et int : où b représente le nombre de bit) signed (par défaut) : variable [ -2 b /2, 2 b /2 – 1 ] unsigned : variable [ 0, 2 b – 1] –Taille mémoire short : réduit le domaine des valeurs possibles –uniquement pour int long : augmente lintervalle [min, max] –uniquement pour int et float selon la norme ANSI –certains compilateurs supportent aussi le type double Attention au type bool

13 12 Types de base Le type char –Un seul type, deux interprétations : caractère lorsque utilisé en conjonction avec les fonctions de gestion de chaîne de caractères et la table Ascii entier autrement

14 13 Types de base Le type int (16 bits) ou short int

15 14 Types de base Le type int (32 bits) ou long int

16 15 Types de base Le type float

17 16 Types de base Le type long float ou double

18 17 Types de base Le type long double Ce nest pas un type standard du C ou du C++, il est seulement disponible sur certains compilateurs (par exemple : VisualC++ et C++Builder)

19 18 Types de base Le type void –Ne permet pas de déclarer une variable –Permet dindiquer au compilateur que le type est indéfini pour les cas suivants : retour de fonction (aucune valeur de retour) paramètres dune fonction (aucun paramètre passé à la fonction) pointeur de type indéfini

20 19 Éléments du langage et règles décriture Les identificateurs –Sert à nommer les : variables étiquettes fonctions –Constitués dune suite de lettres et de chiffres commençant par une lettre le seul autre caractère permis : '_' –Il faut faire attention à : le langage C distingue les majuscules des minuscules la liste des mots réservés Les types externes peuvent être restreint à un maximum de 8 caractères pour certains compilateurs

21 20 Éléments du langage et règles décriture Les mots réservés du langage C autobreakcasecharconstcontinue defaultdodoubleelseenumextern floatforgotoifintlong registerreturnshortsignedsizeofstatic structswitchtypedefunion unsigned void volatilewhile

22 21 Éléments du langage et règles décriture Les commentaires –Pour déterminer une zone de commentaires, on utilise : /* pour le début */ pour la fin –Attention aux : imbrications de commentaires commentaires commençant par //

23 22 Éléments du langage et règles décriture Les instructions –Appel dun opérateur ou dune fonction –Plusieurs instructions peuvent être regroupées par un bloc Les blocs dinstructions –Défini par des accolades { marque le début dun bloc } marque la fin dun bloc {/* début dun bloc dinstructions */ /* instruction 1 */ /*... */ /* instruction n */ }/* fin dun bloc dinstructions */

24 23 Éléments du langage et règles décriture Les expressions –Correspondent à une combinaison déléments tel que des : identificateurs constantes variables tableaux pointeurs structures unions appels de fonctions opérateurs unaires ou binaires... chaque ligne de code excluant celles ayant uniquement des commentaires

25 24 Éléments du langage et règles décriture Les constantes –Entier : octal : commençant par un zéro décimal : tel quel hexadécimal : commençant par un zéro et un x0xCAFE les majuscules et minuscules fonctionnent 0Xcafe –Nombre réel de type float : terminant par f f de type double : tel quel

26 25 Éléments du langage et règles décriture Les constantes –Caractère(s) un seul caractère selon la table de caractères ASCII : entre apostrophes'0' le caractère zéro est équivalent à lentier 48 une chaîne de caractères respectant les normes établies : entre guillemet"GPA665" la chaîne de caractères "GPA665" correspond au tableau de caractères : il existe plusieurs caractères spéciaux : \a - beep - \\ barre oblique inverse \b retour arrière \' apostrophe \fchargement de page \" guillemet \n saut de ligne \? point dinterrogation \r retour en début ligne \nnn valeur ASCII en octal \t tabulation horizontale \xnnn valeur ASCII en hexadécimal \v tabulation verticale \0 fin de la chaîne de caractère

27 26 Éléments du langage et règles décriture Les opérateurs –Unaires – Sont évalués de droite à gauche ++ vareffectue une pré incrémentation -- var effectue une pré décrémentation var ++ effectue une post incrémentation (de gauche à droite) var -- effectue une post décrémentation (de gauche à droite) (type) expspécifie une modification de type (« type cast ») Sizeof() ou sizeof(var ou type)retourne la taille en octet dune variable ou dun type & var retourne ladresse de la variable * expretourne le contenu de ladresse spécifiée (déréférence) + expretourne la valeur dune variable (type numérique) - expretourne la valeur négative dune variable (type numérique) ~ expretourne le complément bit à bit dune variable ! expretourne le complément logique dune variable

28 27 Éléments du langage et règles décriture Les opérateurs –Binaires – Sont évalués de gauche à droite Opérateurs multiplicatifs –exp1 * exp2 retourne le résultat de la multiplication –exp1 / exp2 retourne le résultat de la division –exp1 % exp2 retourne le résultat du modulo (reste de la division entière) Opérateurs additifs –exp1 + exp2 retourne le résultat de laddition –exp1 - exp2 retourne le résultat de la soustraction Opérateurs de décalage de bits –exp1 << exp2 retourne le résultat du décalage à gauche * –exp1 >> exp2 retourne le résultat du décalage à droite * Opérateurs relationnels –exp1 < exp2 retourne le résultat de la comparaison est plus petit que –exp1 > exp2 retourne le résultat de la comparaison est plus grand que –exp1 <= exp2 retourne le résultat de la comparaison plus petit ou égal –exp1 >= exp2 retourne le résultat de la comparaison plus grand ou égal –exp1 == exp2 retourne le résultat de la comparaison est égal à –exp1 != exp2 retourne le résultat de la comparaison est différents de

29 28 Éléments du langage et règles décriture Les opérateurs –Binaires – Sont évalués de gauche à droite (suite) Opérateurs sur les bits –exp1 & exp2 retourne le résultat du ET logique bit à bit –exp1 | exp2 retourne le résultat du OU logique bit à bit –exp1 ^ exp2 retourne le résultat du OU EXCLUSIF logique bit à bit Opérateurs logiques –exp1 && exp2 retourne le résultat du ET logique –exp1 || exp2 retourne le résultat de OU logique Opérateur dévaluation séquentielle –exp1, exp2 effectue les expressions séquentiellement –Ternaire – Est évalué de gauche à droite –exp ? val1 : val2retourne val1 si exp est vraie sinon val2

30 29 Éléments du langage et règles décriture Les opérateurs –Dassignation – Sont évalués de droite à gauche var = expassignation directe var *= exp assignation suite à la multiplication var /= exp assignation suite à la division var %= exp assignation suite au modulo var += exp assignation suite à laddition var -= exp assignation suite à la soustraction var <<= exp assignation suite au décalage de bits à gauche var >>= exp assignation suite au décalage de bits à droite var &= exp assignation suite au ET logique bit à bit var |= exp assignation suite au OU logique bit à bit var ^= exp assignation suite au OU EXCLUSIF logique bit à bit Ils sont tous équivalent à : var = var opérateur exp

31 30 Éléments du langage et règles décriture Les opérateurs –Autres opérateurs ( exp ) - appel de fonction - spécification de priorité dans une expression [ exp ] - retourne un élément spécifique dun tableau (décalage plus déréférence) var. élément - retourne lélément spécifié dune structure (indirection) ou dun type union var -> élément - retourne lélément spécifié par le pointeur dune structure (indirection)

32 31 Éléments du langage et règles décriture Priorité des opérateurs OrdreOpérateurs 1( ) [ ]. -> (signe) -(signe) *(déréférence) &(adresse) ! ~ (type)(« type cast ») sizeof 3*(multiplication) / % 4+(addition) -(soustraction) 5>> << 6== != 7& 8^ 9| 10&& 11|| 12? : 13= += -= *= /= %= &= ^= |= >= 14, Si deux opérateurs possèdent la même priorité, ils seront exécuté de gauche à droite selon lordre dapparition sur la ligne dinstruction

33 32 Éléments du langage et règles décriture Les instructions de contrôle –Linstruction conditionnelle if Attention aux if imbriqués. Utilisez les définitions de bloc. if ( exp ) inst1 [ else inst2 ] if (n != 0) { Mean = Sum / n; } else { printf("Erreur : division par 0"); }

34 33 Éléments du langage et règles décriture Les instructions de contrôle –Linstruction conditionnelle switch switch ( integer-exp ) { [ case constexp1 : inst1 [ break ;] ]... [ case constexpn : instn [ break ;] ] [ default : instd ] }

35 34 Éléments du langage et règles décriture #include #include "Customfunctions.h" void main(void) { while (1) { printf("Quelle operation voulez-vous faire ?\n"); printf("\t1 (Q)uitter\n\t2 (S)auvegarder\n\t 3 (P)oursuivre\n\t4 (M)ettre en veille\n"); char Reponse = toupper(getch());/* Attention */ switch (Reponse) { case 1 : case 'Q' : return; case 2 : case 'S' : SaveData(); case 3 : case 'P' : GetNewData(); AnalyseNewData(); break; case 4 : case 'M' : GotoSleep(); }

36 35 Éléments du langage et règles décriture Les instructions de contrôle –Linstruction itérative while while ( exp ) inst while (Answer == 'Y' || Answer == 'y') { DoSomething(); printf("Voulez-vous poursuivre?"); Answer = getch(); }

37 36 Éléments du langage et règles décriture Les instructions de contrôle –Linstruction itérative do do inst while ( exp ); do { DoSomething(); printf("Voulez-vous poursuivre?"); Answer = getch(); } while (Answer != 'N' && Answer != 'n');

38 37 Éléments du langage et règles décriture Les instructions de contrôle –Linstruction itérative for for ( inst1; exp; inst2 ) inst3 #define N int i, Sum, Data[N]; for ( i = 0, Sum = 0; i < N; i++) { Sum += Data[i]; }

39 38 Éléments du langage et règles décriture Les instructions de contrôle –Les commandes de terminaison sont : break termine linstruction do, for, switch ou while le plus près continue passe à la prochaine itération des instructions do, for ou while goto passe directement à létiquette spécifiée

40 39 Éléments du langage et règles décriture La déclaration détiquettes –La fonction goto doit indiquer une étiquette existante dans le programme. –Les étiquettes sont définies par le deux points –Les étiquettes doivent obligatoirement être mises devant une instruction pour être valide –Malgré le fait quelle est très puissante, linstruction goto nest pratiquement jamais recommandée. identificateur : Erreur007:

41 40 Éléments du langage et règles décriture La déclaration de nouveaux types –Types synonymes avec typedef En fait, ceci ne permet pas la déclaration dun nouveau type mais plutôt dun synonyme pratique ou pertinent pour lusager –Types énumérés avec enum Type consistant à contraindre laffectation de variable à un ensemble de constantes appelées énumérateurs enum [ type ] { liste-enumerations } [ variable ] ; typedef enum [ type ] { liste-enumerations } [ type ] ; typedef declaration-de-type synonyme; typedef unsigned int uint; typedef unsigned char uchar; typedef uchar Pixel; enum JourSemaine { Lundi, Mardi, Mercredi, Jeudi, Vendredi } AujourdHui; typedef enum { Rouge, Vert, Bleu } CouleursPrimairesAdditif; typedef enum CouleursPrimaireSoustractif { Jaune, Magenta, Cyan };

42 41 Éléments du langage et règles décriture La déclaration de nouveaux types –Types structurés avec struct Nouveau type permettant de contenir plusieurs types existants (sous-types) simultanément pour une même variable. Tous les sous-types sont disponibles simultanément. La taille du nouveau type correspond au minimum à la somme des tailles de chacun des sous-types. struct [ type ] { liste-membres } [ variable ] ; typedef struct [ tag ] { liste-membres } type ; struct Individu { char Nom[64]; int Age; float Taille; } Nicolas, Yan, Philippe; typedef struct refNode { float Data; refNode *Next; } LinkedListNode;

43 42 Éléments du langage et règles décriture La déclaration de nouveaux types –Types unis avec union Nouveau type permettant de contenir différents types existants (sous-types) pour une même zone mémoire. Puisque tous les sous-types partagent la même zone mémoire, un seul de ces sous-types est disponible à la fois. La taille du nouveau type correspond au minimum à la taille du plus grand sous-type. union [ type ] { liste-membres } [ variable ] ; typedef union [ tag ] { liste-membres } type ; union Data { float LowPrecision; double HighPrecision; } Temperature; typedef union { char Answer; float Value; } QuestionResult;

44 43 Éléments du langage et règles décriture La déclaration de variables –La déclaration dune seule variable : –La déclaration dun tableau unidimensionnel : –La déclaration dun tableau multidimensionnel : on reviendra plus tard sur les classes de mémorisation [ classe_de_mémorisation ] type identificateur ; type identificateur[ taille du tableau ] ; type identificateur[dim1][dim2][dim_n]... ; static int Sum; float Vector[256]; unsigned char ColorImage[640][480][3];

45 44 Éléments du langage et règles décriture La déclaration de variables –La déclaration dun pointeur : –On peut déclarer plusieurs variables simultanément : Attention à la déclaration simultanée de pointeurs! [ classe_de_mémorisation ] type * identificateur ; type ident1, ident2, ident3; int *PtrData; float n, Sum, Mean, StdDev, Data[256];

46 45 Éléments du langage et règles décriture La déclaration de variables –La déclaration dune variable constante : Une variable constante doit être initialisée à même sa déclaration et ne peut jamais être modifiée. [ classe_de_mémorisation ] const type identificateur = valeur; const float PI = ;

47 46 Éléments du langage et règles décriture Linitialisation de variables –Selon la norme ANSI, le langage C ninitialisent aucun type automatiquement lors de linstanciation de variables –Malgré tout, les compilateurs modernes comme VisualC++ initialisent à 0 uniquement les variables provenant des types de base char, int, float et double les pointeurs, tableaux et types personnalisés ne sont pas initialisés malgré tout, faites attention aux optimisateurs de code

48 47 Éléments du langage et règles décriture Linitialisation de variables –Les variables provenant des types de base peuvent être explicitement initialisées lors de leur déclaration Pour les variables standard : Pour les variables de type union Pour les variables de type struct [ classe_de_mémorisation ] type identificateur = exp; int IPos = 10, INeg = -10; void *RefPtr = NULL; typedef union { float NbrReel; int NbrEntier; } Nombre; Nombre N1 = { f }, N2 = { 101 }; // les bons types ont été assignés [ classe_de_mémorisation ] type identificateur = { exp }; typedef struct { char Nom[256]; int Age; float Taille; } Individu; Individu Pianiste = { "Julius Katchen", 43, 1.80 }; [ classe_de_mémorisation ] type identificateur = { exp1, exp2,... };

49 48 Éléments du langage et règles décriture Linitialisation de variables Pour un tableau unidimensionnel : Pour un tableau multidimensionnel : type identificateur[ [taille du tableau] ] = { exp1, exp2, exp3,... }; type ident[dim 1][dim n]... = {... { exp1,... }, { expn,... },... }; float ResultatsLabGPA665[3] = { 89.0f, 97.0f, 100.0f }; float ResultatsExamGPA665[] = { 78.0f, 84.5f };/* intéressant */ int Input[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };

50 49 Éléments du langage et règles décriture Éléments de langage particuliers aux pointeurs –Opérateurs dadressage et de déréférence On utilise & pour obtenir ladresse dune variable ou dune fonction préalablement allouée On utiliser * (la déréférence) pour accéder au contenu « pointé » par une adresse int Nombre1, Nombre2; int *PtrNbr; Nombre1 = 666; PtrNbr = &Nombre1; Nombre2 = *PtrNbr;

51 50 Éléments du langage et règles décriture Éléments de langage particuliers aux types composés –Accès aux membres des types composés union ou struct On utilise lopérateur dindirection. (point) pour accéder à un membre particulier Pour les accès via un pointeur, on peut utiliser : –une combinaison du point et de la déréférence –lopérateur dindirection -> typedef struct { char Nom[256]; int Age; float Taille; } Individu; Individu Employe1, Employe2, *RefEmp1, *RefEmp2; Ref1Emp = &Employe1; Ref2Emp = &Employe2 RefEmp1->Age = 2 * (*RefEmp2).Age;

52 51 Éléments du langage et règles décriture Éléments de langage particuliers aux tableaux –Accès aux éléments dun tableau On utilise lopérateur de déréférence avec décalage [ ] pour accéder à un élément spécifique dun tableau On peut aussi utiliser lopérateur de déréférence avec larithmétique des pointeurs float NotesExam[] = { 84.0f, 68.0f, 90.5f }; float PonderationsExam[] = { 0.30f, 0.30f, 0.40f }; float ResultatFinal = 0.0f; for (int i = 0; i < 3; i++) {/* Attention a la declaration de i */ ResultatFinal += NotesExam[i] * PonderationsExam[i]; }

53 52 Éléments du langage et règles décriture Éléments de langage particuliers typedef char String[256]; typedef union { String Nom; int NoRef; } IDRef; struct Individu { IDRef ID; int Age; String SignesDistinctifs; } Professeur, ChargeDeCours, ChargeDeLab; Individu *Ref[3] = { &Professeur, &ChargeDeCours, &ChargeDeLab }; strcpy(Professeur.ID.Nom, "Mohamed Cheriet"); ChargeDeCours.ID.NoRef = 12345; strcpy(ChargeDeLab.ID.Nom, "Yan Levasseur"); ChargeDeLab.Age = 25; strcpy(ChargeDeLab.SignesDistinctifs, "Intellectuellement superieur"); (*Ref[0]).Age = Ref[2]->Age << 1;

54 53 Éléments du langage et règles décriture Les fonctions –Les fonctions sont des entités permettant de regrouper plusieurs instructions et expressions en un sous-ensemble disjoint, autonome et fonctionnel. –Entre autre, elles aident à la lisibilité, la modularité et la réutilisabilité du code /* Syntaxe moderne */ [ classe-mémorisation ] type-retour identificateur( [ type1 param1,... ] ) { exp1 exp2... [ return [ ( ] [ exp-retour ] [ ) ] ; ] } float Square(float Data) { return Data * Data; }

55 54 Éléments du langage et règles décriture Les instructions au préprocesseur –Ces instructions servent à donner des instructions préalables au compilateur. –Elles sont faciles à reconnaître car elles sont tous précédés de # Inclusion de fichiers avec #include. Permet de faire référence au contenu dun ou plusieurs autres fichiers externes. #include "nom_de_fichier"/* recherche locale + catalogue système */ #include /* recherche catalogue système uniquement */ #include "mes_propres_fonctions.h" #include void main(void) { printf("Le racine carre de 9 est : %lf", sqrt(9.0)); printf("La racine cubique de 27 est : %lf", cubicroot(27.0)); }

56 55 Éléments du langage et règles décriture Les instructions au préprocesseur Les commandes de compilation conditionnelle sont : –#if équivalent du if –#elif équivalent à un else if –#else équivalent au else –#endif indique la terminaison du bloc if –#ifdef si une étiquette est définie –#ifndef si une étiquette nest pas définie #if exp1 inst1 #elif exp2 inst2 #else inst3 #endif

57 56 Éléments du langage et règles décriture Les instructions au préprocesseur –#define et #undef permettent de déclarer de nouvelles définitions ou dannuler des définitions existantes. Ces définitions permettent au programmeur de nommer des éléments du programme comme des constantes et des étiquettes de référence. #define identificateur expression #undef identificateur #include "Constantes.h" void main(void) { printf("Le nombre pi est : %0.6lf", PI); } #ifndef _CONSTANTES_H #define _CONSTANTES_H #define PI #endif Constantes.hProgramme.c

58 57 Éléments du langage et règles décriture Les instructions au préprocesseur #define permet aussi de définir des macro. Les macros ressemblent à des fonctions sans définition de type. #define nom_macro(param1, param2,...) expression #define Max(Val1, Val2) ((Val1) > (Val2) ? (Val1) : (Val2)) void main(void) { int N1 = 100, N2 = 500; printf("Le maximum entre les nombres %d et %d est : %d", N1, N2, Max(N1, N2)); }

59 58 Éléments du langage et règles décriture Les instructions au préprocesseur Pourquoi éviter les macros ? En fait, lutilisation du #define correspond à un copier/coller avant la compilation. #define Max(Val1, Val2) ((Val1) > (Val2) ? (Val1) : (Val2)) #define Square(Val) a*a void main(void) { int A, *B; A = Max('a', B);/* Compile malgré les types incompatibles */ int i = 1, j = 2; A = Max(++i,++j);/* On sattend à 3 alors quon obtient 4 */ double X = 3, Y; Y = Square(X + 1);/* On sattend à 16 alors quon obtient 7 */ }

60 59 Principales fonctions provenant des bibliothèques standard stdio.h –fopenouverture dun fichier –fclosefermeture dun fichier –ferrorvérifie si il y a eu une erreur avec un fichier –feofvérifie si un fichier est arrivé à la fin –fgetposdétermine un indicateur pour la position courante dun fichier –fsetpospositionne un fichier selon un indicateur de position –ftellretourne la position dun fichier –fseekpositionne un fichier selon un décalage spécifié –freadlit des données provenant dun fichier à partir de la position courante –fwriteécrit des données dans un fichier

61 60 Principales fonctions provenant des bibliothèques standard stdio.h –fgetslit une chaîne de caractères provenant dun fichier –getslit une chaîne de caractères provenant de lentrée standard –fputsécrit une chaîne de caractères dans un fichier –putsécrit une chaîne de caractères à la sortie standard –fgetclit un caractère provenant dun fichier –getclit un caractère provenant de lentrée standard –fputcécrit un caractère dans un fichier –putcécrit un caractère à la sortie standard –stdinconstante de redirection vers lentrée standard –stdoutconstante de redirection vers la sortie standard –stderrconstante de redirection vers la sortie derreur standard

62 61 Principales fonctions provenant des bibliothèques standard stdio.h –printf*écrit une chaîne de caractères mise en forme vers la sortie standard –sprintfécrit une chaîne de caractères mise en forme dans une variable de type chaîne de caractères –fprintfécrit une chaîne de caractères mise en forme dans un fichier –scanf*lit une chaîne de caractères mise en forme provenant de lentrée standard –sscanflit une chaîne de caractères mise en forme provenant dune variable de type chaîne de caractères –fscanflit une chaîne de caractères mise en forme provenant dun fichier

63 62 Principales fonctions provenant des bibliothèques standard stdlib.h –NULLdéclaration du pointeur NULL –mallocallocation dun bloc de mémoire –callocallocation dun bloc de mémoire initialisé à zéro –reallocréallocation dun bloc de mémoire –freelibération dun bloc de mémoire alloué –systemexécute une commande du système dexploitation –exittermine lexécution du programme en cours –atoiconversion dune chaîne de caractères en valeur numérique entière –atofconversion dune chaîne de caractères en valeur numérique réelle

64 63 Principales fonctions provenant des bibliothèques standard memory.h –memcpycopie un bloc mémoire dun endroit à un autre –memcmpcompare deux blocs mémoire caractère par caractère –memsetinitialise chaque octet dun bloc mémoire par la valeur dun octet spécifié

65 64 Principales fonctions provenant des bibliothèques standard math.h –cosretourne le cosinus dun angle en radian –acosretourne, en radian, larcosinus dun nombre –coshretourne le cosinus hyperbolique dun angle en radian –sinretourne le sinus dun angle en radian –asinretourne, en radian, larcsinus dun nombre –sinhretourne le sinus hyperbolique dun angle en radian –tanretourne, la tangente dun angle en radian –atanretourne, en radian, la tangente dun nombre –atan2retourne, en radian, la tangente dun nombre –tanhretourne la tangente hyperbolique dun angle en radian

66 65 Principales fonctions provenant des bibliothèques standard math.h –powcalcule un nombre à la puissance dun autre –expcalcule lexponentiel dun nombre (e n ) –logcalcule le logarithme naturelle dun nombre –log10calcule le logarithme en base 10 dun nombre –sqrtcalcule la racine carrée dun nombre –abscalcule la valeur absolue dun nombre entier –fabscalcule la valeur absolue dun nombre réel –diveffectue une division entière –floorretourne larrondi inférieur dun nombre –ceilretourne larrondi supérieur dun nombre –sranddétermine un point de départ pour la génération dun nombre pseudo aléatoire –randgénère un nombre pseudo aléatoire

67 66 Principales fonctions provenant des bibliothèques standard string.h –strlenretourne la longueur dune chaîne de caractères –strsetinitialise une chaîne de caractères à un caractère spécifique –strcpycopie une chaîne de caractères –strncpycopie n caractères provenant dune chaîne de caractères –strcmpcompare deux chaînes de caractères –strncmpcompare n caractères provenant de deux chaînes de caractères –strcatconcatène deux chaînes de caractères –strncatconcatène n caractères dune chaîne de caractères dans une autre

68 67 Principales fonctions provenant des bibliothèques standard string.h –strchrrecherche un caractère spécifique dans une chaîne de caractères –strstrrecherche une chaîne de caractères spécifique à lintérieur dune chaîne de caractères –strtokrecherche la prochaine occurrence dun « token » dans une chaîne de caractères

69 68 Principales fonctions provenant des bibliothèques standard ctype.h –isdigitidentifie si un caractère est numérique –isalphaidentifie si un caractère est alphabétique –isloweridentifie si un caractère est en minuscule –isupperidentifie si un caractère est en majuscule –tolowerconvertie un caractère à un caractère minuscule –toupperconvertie un caractère à un caractère majuscule

70

71 70 Portée et durée de vie des variables Gestion dune variable par le compilateur C –Les 2 points essentiels suivants : le domaine de validité dune variable (couramment appelé le « scope », la portée ou la visibilité) la durée de vie de la variable (cest-à-dire la durée pendant laquelle la variable existe vraiment) sont déterminés par : lemplacement de la définition de la variable la classe de mémorisation utilisée à sa déclaration (« storage class »)

72 71 Portée et durée de vie des variables La notion de variable globale et locale est directement liée à lemplacement de la déclaration –Par définition, une variable est visible par toutes les fonctions et les expressions de code qui suit sa déclaration si ces dernières sont à un niveau de bloc égal ou inférieur. –Les variables globales : Elles sont déclarées hors de toute fonction Elles sont donc connues, valides et utilisables dans tout le fichier où elle est définie; cela à partir de lendroit de sa déclaration. Selon sa classe de mémorisation, elle peut même sétendre à dautres fichiers. –Les variables locales : Elles sont définies à lintérieur dun bloc ou dune fonction. Par définition, elles sont donc visibles, valides et utilisables uniquement pour ces blocs ou fonctions.

73 72 Portée et durée de vie des variables Description des classes de mémorisation –Chaque variable possède une seule classe de mémorisation parmi les suivantes : auto static extern register –Les variables globales ne peuvent être que des classes extern ou static

74 73 Portée et durée de vie des variables Description des classes de mémorisation pour les variables locales : –auto (pour « automatic ») est la classe de mémorisation par défaut. Le bloc dans laquelle elles sont définie détermine leur visibilité et leur durée de vie. –static Cette classe de mémorisation indique que la portée dune variable est la même que pour une variable auto mais que sa durée de vie correspond à celle du programme. [ auto ] type identificateur ; auto int Sum; float Average; static type identificateur ; static int Flag;

75 74 Portée et durée de vie des variables Description des classes de mémorisation pour les variables locales (suite) : –extern Cette classe de mémorisation est particulière car elle nentraîne pas dallocation mémoire. Elle fait plutôt référence à une autre variable existante du même nom ailleurs dans le programme. –register Ces variables obéissent aux même règles que celles de type auto. La différence vient du fait que le compilateur tente de la placé à même les registres du CPU plutôt que dans la mémoire de travail. extern type identificateur ; extern int VersionNoSofware; register type identificateur ; register int i, j, k, l;

76 75 Portée et durée de vie des variables Description des classes de mémorisation pour les variables globales : –extern Est la classe de mémorisation par défaut. Leur portée est au moins le fichier où elle sont déclarées et peut être étendue à plusieurs fichiers. Leur durée de vie sont celle du programme. –static Leur durée de vie est la même que pour les variables globales extern. Leur portée inclue le fichier de leur déclaration mais ne peut être étendue à dautres fichiers. extern type identificateur ; extern int GlobalInformation; static type identificateur ; static int RegionalInformation;

77 76 Portée et durée de vie des variables Résumé sur la portée et la durée de vie des variables :

78 77 Portée et durée de vie des variables #include int VarGlobale1 = 1;/* Variable globale */ void main(void) { int VarLocaleMain = 2;/* Variable locale au main */ if (VarLocaleMain >= 0) { int VarLocalBlocIf = 321;/* Variable locale */ }/* visible dans le bloc if */ printf("%d, %d, %d\n", VarGlobale1, VarLocaleMain, Fonction(54321)); } int VarGlobale2 = 123;/* Variable globale invisible par le main*/ int Fonction(int Val) { int VarLocaleFonction = Val;/* Variable locale à la fonction */ return VarLocaleFonction * VarGlobale1 * VarGlobale2; }

79

80 79 Rappel général sur les pointeurs Un pointeur est une variable dont le contenu a pour valeur une adresse en mémoire. Le type du pointeur correspond au type de la variable pointée. En fait, ce typage ne sert au compilateur quà souligner au programmeur les erreurs potentielles lorsquelles surviennent. Tous les pointeurs sans exception ont 32 bits (en considérant un environnement 32 bits tel que Windows) Attention à lopérateur * puisquil est le seul élément syntaxique du langage C à avoir 3 sens différents selon le contexte : –La multiplication dans une expression mathématique –La déclaration dun pointeur dans une déclaration de variable –La déréférence dun pointeur pour accéder au contenu de ladresse mémoire référencée

81 80 Représentation schématique de la mémoire Afin de bien comprendre létat des pointeurs il est pratique de schématiser létat de la mémoire. Une astuce efficace consiste à observer lévolution dun programme ligne par ligne en faisant le suivi de toutes les variables par la représentation par case. Pour chaque allocation ou libération dune variable, on crée ou détruit une case représentant la variable à suivre. Ensuite, pour chaque affectation, comparaison ou manipulation de ces variables, on sassure que les types sont compatibles et que chacune des instructions correspondent bien à ce qui est souhaité. Variable standard Variable de type constant On assigne les adresses arbitrairement à titre de référence.

82 81 Représentation schématique des pointeurs Que pensez-vous de ce petit programme? void main(void) { int V1, *P1;/* 01 */ int *P2, V2;/* 02 */ /* 03 */ P1 = &V1;/* 04 */ P2 = &V2;/* 05 */ /* 06 */ V1 = 100;/* 07 */ V2 = *P1;/* 08 */ *P2 = 2.0 * *P1;/* 09 */ /* 10 */ int V3 = P1;/* 11 */ /* Attention! Est-ce que ça compile? */ int *P3 = V1;/* 12 */ /* Attention! Est-ce que ça compile? */ }

83 82 Pointeurs multiples Partant du fait quun pointeur est une variable dont le contenu indique lendroit en mémoire du contenu dune autre variable de nimporte quel type. Quarrive-t-il si nous désirons utiliser un pointeur indiquant un autre pointeur? On déclare les pointeurs multiples par lutilisation de plusieurs *. En fait, on utilise autant dastérisque que dindirection nécessaire. [ Classe_de_memorisation ] type ***...* identificateur ; int Value; int *PtrLevel1, **PtrLevel2, ***PtrLevel3; PtrLevel1 = &Value; PtrLevel2 = &PtrLevel1; PtrLevel3 = &PtrLevel2 Value = 10; *PtrLevel1 = 100; **PtrLevel2 = 1000; ***PtrLevel3 = 10000;

84 83 Pointeurs et structures On revient sur les opérateurs dindirection pour sassurer de bien les maîtriser. #define STRING_MAX 256 typedef struct { int NoCivique;/* No civique */ char Rue[STRING_MAX];/* Nom de la rue */ char Ville[STRING_MAX];/* Nom de la ville */ } Adresse; typedef struct { char Nom[STRING_MAX];/* Nom de lindividu */ int Age;/* Age en annee de lindividu */ int *RefNoCoursGPA;/* Numero du cours suivi en GPA */ Adresse *RefAd;/* Reference vers une adresse */ } Individu; /* Declaration et initialisation */ Adresse Residence1 = { 1100, "Notre-Dame Ouest", "Montreal" }; Adresse Residence2 = { 666, "Rue de la fin du monde", "St-Isidor de lApocalypse" }; int CoursInteressantDeGPA = 665; Individu Justin = { "Justin B. Sil", 24, &CoursInteressantDeGPA, &Residence1 }; Individu Jay = { "Jay Latrouille", 23, &CoursInteressantDeGPA, &Residence2 }; Individu *Personne = &Jay; /* Quelques instructions */ int NoCiviqueQuelconque = Jay.RefAd->NoCivique;/* ou (*Jay.RefAd).NoCivique */ int NoCoursQuelconque = *Justin.RefNoCoursGPA;/* est-ce correct sans parathèse ? */ char NomVille[STRING_MAX], *PtrCible = NomVille; char *PtrSource = Personne->RefAd->Ville; /* ou (*(*Personne).RefAd).Ville */ int i = 0; while ((*PtrCible++ = *PtrSource++) != '\0' && i++ < STRING_MAX);

85 84 Rappel général sur les tableaux Un tableau est un ensemble de variable de même type. Un tableau est toujours constitué de deux parties : –lespace des données qui est toujours contiguë en mémoire (que ce soit pour un tableau unidimensionnel ou multidimensionnel) –une référence de type pointeur indiquant toujours le début du tableau int Tableau[5] = { 10, 100, 1000, 10000, };

86 85 Rappel général sur les tableaux Contrairement aux allocations dynamiques que nous verrons plus loin, les allocations statiques de tableaux ne génèrent pas dallocation mémoire pour le pointeur de référence. Ce pointeur est constant et ne peut être modifié. Les tableaux réservent le nombre doctets suivant : taille tableau = sizeof(type) * nombre déléments Quels sont les tailles des tableaux suivants? unsigned short int Data1[5]; int Data2[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; double Data3[10][10]; typedef struct { char Nom[256]; char Age; float Poids; int NoReference; } Individu; Individu Pierre[1], Jean[100], Jacques[5][10][2];

87 86 Relation étroite entre pointeurs et tableaux Puisque tous les tableaux sont référencés par un pointeur, il est souvent pratique et pertinent daffecter ladresse de départ dun tableau à un pointeur. Les tableaux sont toujours passés aux fonctions par leur adresse de départ. Il est impossible de passer un tableau par son contenu à une fonction. Il est aussi impossible quune fonction retourne le contenu dun tableau, elle ne peut que retourner ladresse du tableau Dans le but daccélérer certains processus, il est parfois efficace dexécuter certaines opérations sur un tableau à laide des pointeurs et de ne pas utiliser les opérateurs [ ] pour chaque accès au tableau.

88 87 Arithmétique sur les pointeurs Les opérateurs daddition, de soustraction, dincrémentation ou de comparaison sont permis sur les pointeurs. Ils permettent le calcul de décalage entre une adresse de base et une adresse souhaitée. Tous ces calculs sont automatiquement gérés par le compilateur en fonction de la taille du type de référence du pointeur. #define TABLE_SIZE5 const float CND_TO_USD = 1.2f float CNDCost[TABLE_SIZE] = { 10.25f, 2.12f, f, 32.77f, 0.12f }; short int Quantity[TABLE_SIZE] = { 3, 12, 2, 8, 193 }; float GlobalUSDCost; unsigned short int i; /* Cout global en dollard americain */ GlobalUSDCost = 0.0f; for (i = 0; i < TABLE_SIZE; i++) { GlobalUSDCost += CNDCost[i] * CND_TO_USD * Quantity[i]; } i = 0; GlobalUSDCost = 0.0f; short int *CurrentQuantity = Quantity, *PostLastQuantity = Quantity + TABLE_SIZE; while (CurrentQuantity < PostLastQuantity) { GlobalUSDCost += *(CNDCost + i++) * CND_TO_USD * *CurrentQuantity++; }

89 88 Arithmétique sur les pointeurs Que se passe-t-il exactement avec ces lignes de code? pour faire le suivi, on retire les lignes qui ne compilent pas et celles qui causent un débordement de mémoire int A[] = { 1, 2, 3, 4, 5 }; int a = 0x100; int *pA = A, *pa = &a, **ppA = &pA; a = A;/* 01 */ a = *A;/* 02 */ a = A[0] * 0;/* 03 */ a = pA;/* 04 */ a = *pA;/* 05 */ a = pA[0] + 0;/* 06 */ a = pa;/* 07 */ a = *pa * 0;/* 08 */ a = pa[0];/* 09 */ *pa = *pA;/* 10 */ *(pA + 3) = *(pa + 3);/* 11 */ A[3] = a[3];/* 12 */ a = A[5];/* 13 */ A[1] = A[2];/* 14 */ *(pA – 1) = a;/* 15 */ A = pA[2];/* 16 */ a *= *A * *(A + *(pA + 1));/* 17 */ a = --*pA++;/* 18 */ (A + 2)[2] = (*pa + 2) * *(pA – 1);/* 19 */ *(ppA[0] + 3) = (*ppA + 2)[0];/* 20 */

90 89 Particularité des chaînes de caractères Le langage C et même le langage C++ noffrent aucun type de base correspondant à une chaîne de caractères (string). (par contre, il existe plusieurs implémentations de classes performantes pour en faire la gestion en C++ – voir la bibliothèque STL à titre dexemple) La façon de gérer les chaînes de caractères est simplement par lusage dun tableau de type char. Par convention, on utilise le caractère '\0' (correspondant à la valeur 0) pour identifier la fin dune chaîne de caractères. Ainsi, la longueur dune chaîne de caractères ne correspond pas nécessairement à la place quelle prend en mémoire. La déclaration dune chaîne de caractères constante et toutes les fonctions des bibliothèques standard respectent cette convention. Cest pourquoi il est important de la suivre. char Discipline[] = "Gymnastique"; char Prenom[256] = "Kassandra";

91 90 Gestion statique et dynamique de la mémoire Les compilateurs basés sur le langage C gèrent automatiquement toutes les allocations mémoire provenant des déclarations standard. Ils gèrent à la fois les allocations, les initialisations (lorsque pratiquées) et les libérations de la mémoire au moment opportun. Puisque toutes ces opérations sur la mémoire sont connues au moment de la compilation, le programme, lorsquil est compilé et généré, contient déjà toutes les instruction de gestion de la mémoire (gestion fixe et immuable) Cest ce mécanisme quon appel la gestion statique de la mémoire.

92 91 Gestion statique et dynamique de la mémoire Avec les fonctions malloc et free, il est possible de faire soi même la gestion de la mémoire au moment opportun – cest ce quon appel la gestion dynamique de la mémoire. Il est essentiel de libérer soi même la mémoire réservée. Sans quoi votre programme présentera des fuites de mémoire (« memory leak »). Aucune allocation dynamique ne fait linitialisation du contenu des variables créées. void * malloc(int taille-en-octet); void free(void * bloc-memoire); int *VariableQuelconque = (int*) malloc(sizeof(int));... *VariableQuelconque = 10;... free(VariableQuelconque);

93 92 Gestion statique et dynamique de la mémoire Alors pourquoi utiliser la gestion dynamique de la mémoire? Cette liberté supplémentaire, malgré le fait quelle complexifie la lecture du code, permet de créer des programmes : –plus flexibles; –plus performants; –plus modulaires; –mieux structurés.

94 93 Gestion statique et dynamique de la mémoire Lun des exemples les plus flagrant est linstanciation dun tableau : –Les allocations statiques de tableaux nécessitent de connaître la taille du tableau au moment de la compilation (« compile time »). Puisquil arrive souvent quon ne connaisse pas la taille réelle requise pour un tableau au moment de la compilation, il est courrant de déterminer la taille de ce dernier égal au pire des cas possibles. Ainsi, cette pratique engendre souvent des contraintes importantes : mémoire réservée inutilement qui peut ne jamais servir (ou très rarement) le tableau alloué peut ne pas pouvoir accueillir toutes les données requises pour une application => le programme est limité et ne peut réaliser toutes les tâches auxquelles il est destinées –Les allocations dynamiques permettent dallouer un tableau de la taille voulue à linstant voulu (au « run time »). Les possibilités sont vastes et permettent dallouer exactement la mémoire requise et de gérer celle-ci de façon optimale.

95 94 Gestion statique et dynamique de la mémoire On se sert souvent des allocations dynamiques pour allouer des tableaux dont la taille est indéfinie au moment de la compilation. Il existe une différence notable entre un tableau alloué statiquement et dynamiquement : la référence du tableau nécessaire à lexécution du programme. int TableauStatique[3] = { 0, 1, 2 }; int *TableauDynamique = (int*) malloc(sizeof(int) * 3);

96 95 Gestion statique et dynamique de la mémoire Puisque le contenu de tout les tableaux statique est contiguë en mémoire (quil soit unidimensionnel ou multidimensionnel), cest le compilateur qui gère les décalages selon ladresse de départ pour chaque accès mémoire. Pour les allocations dynamiques de tableaux multidimensionnels, le compilateur ne peut calculer les décalages souhaités étant donné quil ne connaît pas la taille nominale de chacune des dimensions au moment de la compilation. Il existe alors deux solutions qui se présentent : –la gestion explicite des décalages par le programmeur; –la déclaration de plusieurs tableaux en cascade.

97 96 Gestion statique et dynamique de la mémoire const int TABLE_SIZE_X = 2, TABLE_SIZE_Y = 3; float StaticMatrix[TABLE_SIZE_X][TABLE_SIZE_Y]; float *DynMatrixByOffset; float **DynMatrixByCascade; int i, x, y; /* Allocation dynamique par gestion de décalage */ DynMatrixByOffset = (float*) malloc(sizeof(float) * TABLE_SIZE_X * TABLE_SIZE_Y); /* Allocation dynamique par cascade */ DynMatrixByCascade = (float**) malloc(sizeof(float*) * TABLE_SIZE_X); for (i = 0; i < TABLE_SIZE_X; i++) DynMatrixByCascade[i] = (float*) malloc(sizeof(float) * TABLE_SIZE_Y); /* Initialisation de tous les elements de chaque matrices a 0, 1 ou 2 */ for (x = 0; x < TABLE_SIZE_X; x++) { for (y = 0; y < TABLE_SIZE_Y; y++) { StaticMatrix[x][y] = 0; DynMatrixByOffset[x + y * TABLE_SIZE_X] = 1; /* gestion explicite des décalages */ DynMatrixByCascade[x][y] = 2; } /* Liberation de la matrice dynamique par gestion de décalage */ free(DynMatrixByOffset); /* Liberation de la matrice dynamique par cascade */ for (i = 0; i < TABLE_SIZE_X; i++) free(DynMatrixByCascade[i]); free(DynMatrixByCascade);

98 97 Gestion statique et dynamique de la mémoire

99

100 99 En général Paramètres dune fonction : –on appel la signature la liste des types qui sont passés à une fonction –tous les types peuvent être passés à une fonction à titre dargument mais : les tableaux doivent être passés par leur adresse attention aux structures qui sont copiés bit à bit –il est possible de spécifier labsence de paramètre par : lusage du mot clé void en laissant lintérieure des parenthèses vide –on appel fonctions ellipsis celles dont le nombre dargument est variable (comme printf et scanf – nécessite... à la fin de la liste darguments au moment de la déclaration) Fonction1();/* retourne : int - parametres : aucun */ void Fonction2(void);/* retourne : rien - parametres : aucun */ void* Fonction3(void*);/* retourne : pointeur void – parametres : pointeur void */ float Fonction4(int, int);/* retourne : float - parametres : 2 int */

101 100 En général Type de retour : –une fonction peut retourner nimporte quel type mais : les tableaux sont toujours retournés par leur adresse attention aux types structurés qui sont copié bit-à-bit –si aucun type de retour nest spécifié, le type de retour par défaut est lentier – int –lusage du mot clé void comme type de retour indique que la fonction ne retourne aucune valeur

102 101 En général Terminer une fonction –linstruction return permet la terminaison dune fonction à nimporte quel endroit –return peut retourner une valeur ou non dépendamment du type de retour spécifié lors de la déclaration –linstruction return est facultative pour terminer une fonction ne retournant aucune valeur. return;/* lutilisation du return a la fin dune fonction est facultatif */ return Resultat;/* lorsquon retourne une valeur, on peut la mettre ou non entre... */ return(Result);/* parenthèse */

103 102 Portée des fonctions De façon semblable à la déclaration des variables, laccessibilité ou la visibilité des fonctions est déterminée par : –lemplacement de la déclaration –la classe de mémorisation utilisée à sa déclaration (« storage class ») Lemplacement de la déclaration dune fonction détermine le point à partir duquel celle-ci peut être utilisée. Comme certains types de données, une fonction peut être déclarée avant dêtre définie. On utilise souvent les « header files » (*.h) pour déclarer les fonctions.

104 103 Portée des fonctions #include struct Individu;/* Individu est declare avant sa definition */ int Age(Individu *I, int AnneeCourante);/* Prototype de fonction déclaré bien avant */ struct Individu { char Prenom[32]; char Nom[32]; int AnneeNaissance; }; void main(void) { char* Nom(Individu*, char*);/* Prototype de fonction déclaré à linterne */ /* Le nom des variables est facultatif */ Individu Scientifique = { "Albert", "Einstein", 1879}; char Temp[256]; printf("Monsieur %s a %d an(s)", Nom(&Scientifique, Temp), Age(&Scientifique, 2006)); } int Age(Individu *I, int AnneeCourante) { return AnneeCourante - I->AnneeNaissance; } char* Nom(Individu *I, char *CC) { sprintf(CC, "%s %s", I->Prenom, I->Nom); return CC; } Monsieur Albert Einstein a 127 an(s) - aucune entrée - Entrée Sortie

105 104 Portée des fonctions Description des classes de mémorisation des fonctions : –extern (par défaut) La fonction est une entité globale et peut être utilisée partout où elle est référencée. –static Lutilisation de la fonction est limitée dans le module où elle a été définie. [ extern ] type-retour identificateur( [ type1 param1,... ] )... extern int NombreDeCaractere(char *ChaineCaracteres); [ static ] type-retour identificateur( [ type1 param1,... ] )... static void InverserChaineCaracteres(char *ChaineCaracteres);

106 105 Mécanismes dappel dune fonction Lorsquune fonction est appelée, un mécanisme est mis en action afin de sauvegarder momentanément plusieurs informations nécessaires au déroulement du programme. Ces informations servent au microprocesseur à faire le suivi adéquat du programme. On utilise la pile du système pour y enregistrer temporairement les informations nécessaires. Tous les appels de fonction gérés par le compilateur fonctionnent de la même façon, peu importe leur nature. On nomme : –fonction appelante celle qui appel –fonction appelée celle qui est appelée

107 106 Mécanismes dappel dune fonction Pour chaque appel de fonction : –Un ensemble dinformation sur létat courant du programme, nommé enregistrement dactivation, est mis sur la pile. On y retrouve principalement : Ladresse de retour de la fonction appelante Le résultat de la fonction appelée Tous les paramètres de la fonction appelée Toutes les variables locales de la fonction appelée Létat courant des registres de la fonction appelante qui sont utilisés par la fonction appelée –Trois registres sont systématiquement utilisés : ESP – stack pointer EBP – Base pointer EIP – Instruction Pointer

108 107 Mécanismes dappel dune fonction Les étapes du mécanisme dappel dune fonction sont : Que manque-t-il?

109 108 Mécanismes dappel dune fonction La valeur de retour est passée à titre de premier paramètre de la fonction. cest-à-dire, comme dernier élément empilé sur la pile Par convention, ce paramètre possède toujours 4 octets (32 bits). Quarrive-t-il si la valeur de retour spécifiée ne correspond pas aux 4 octets utilisés? –Si la valeur de retour est plus petite (comme un char), on utilise tout de même les 4 octets. –Si la valeur de retour est plus grande (comme un double), on porte en mémoire haute la valeur de la variable et on utilise les 4 octets réservés comme pointeurs pour indiquer cette ladresse de cette position.

110 109 Mécanismes dappel dune fonction Un exemple assembleur serait plus que pertinent...

111 110 Généralité du langage C Comment est exécuté un programme int i, n, Sum; n = 1234; Sum = 0; for (i = 0; i < n; i++) Sum += i; int i, n, Sum; n = 1234; Sum = 0; for (i = 0; i < n; i++) Sum += i;

112 111 Le passage des paramètres à une fonction Il existe trois méthodes de passage de paramètres à une fonction : –Passage par valeur (« call by value ») les valeurs des paramètres d'appel sont recopiés dans la fonction appelée ainsi la fonction appelée travaille avec une copie des paramètres en fait, c'est le seul mode de passage de paramètres réellement existant en C

113 112 Le passage des paramètres à une fonction … trois méthodes de passage de paramètres (2 ième ): –Passage par référence (« call by reference ») dans ce mode de passage de paramètres, la fonction appelée travail avec les références des paramètres d'appel il existe deux avantages importants de ce mode de transmission –il évite des copies coûteuses des paramètres de tailles importantes –il permet la modification de variables externes à la fonction fondamentalement, ce type de passage est inexistant en C (contrairement au C++ avec ses références) cest lutilisation des pointeurs qui permet, indirectement, dobtenir un comportement qui sapproche du passage par référence

114 113 Le passage des paramètres à une fonction … trois méthodes de passage de paramètres (3 ième ): –Passage par nom (« call by name ») c'est le mode de transmission utilisé par le préprocesseur avec les arguments des macros dans ce mode, les paramètres de l'appel seront textuellement substitués aux diverses occurrences des paramètres formels (sans validation aucune – ni même la vérification de compatibilité des types)

115 114 Le passage des paramètres à une fonction #include void IncrementAndShow(int V1, int V2) { V1++;V2++; printf("Valeur des variables dans IncAndShow : \t%d\t%d\n", V1, V2); } void Swap(int *V1, int *V2) { int VTemp = *V2; *V2 = *V1; *V1 = VTemp; } #define Show(V1, V2) printf("Valeur des variables dans Show : \t%d\t%d\n", V1, V2) void main(void) { int V1 = 5, V2 = 10; printf("Valeur des variables dans main (1) : \t%d\t%d\n", V1, V2); IncrementAndShow(V1, V2); printf("Valeur des variables dans main (2) : \t%d\t%d\n", V1, V2); Swap(&V1, &V2); printf("Valeur des variables dans main (3) : \t%d\t%d\n", V1, V2); Show(--V1, V2++); printf("Valeur des variables dans main (4) : \t%d\t%d\n", V1, V2); } Valeur des variables dans main (1) : 5 10 Valeur des variables dans IncAndShow : 6 11 Valeur des variables dans main (2) : 5 10 Valeur des variables dans main (3) : 10 5 Valeur des variables dans Show : 9 5 Valeur des variables dans main (4) : 9 6 Valeur des variables dans main (1) : 5 10 Valeur des variables dans IncAndShow : 6 11 Valeur des variables dans main (2) : 5 10 Valeur des variables dans main (3) : 10 5 Valeur des variables dans Show : 9 5 Valeur des variables dans main (4) : aucune entrée - Entrée Sortie

116 115 Fonctions récursives Il existe deux types de solution pour résoudre des problèmes nécessitant plusieurs calculs similaires et répétitifs : les solutions itératives et récursives. Dans les deux cas, un mécanisme différent est mis en place afin dexécuter plusieurs fois la même séquence dinstruction. Ces mécanismes se traduisent par : –des boucles de contrôle pour les solutions itératives implique des solutions linéaires (des solutions non linéaires sont possibles avec des artifices) –des fonctions qui sappellent elles même implique des solutions linéaires et non linéaires

117 116 Fonctions récursives Dun point de vue théorique, lorsquune solution récursive est bien structurée, elle est un moyen puissant et unique de résoudre des problèmes. Par définition, la récursivité est une technique de traitement dune tâche T par le traitement dune tâche T –semblable à lapproche descendante –la différence réside dans le fait que la tâche T est de même nature que la tâche T Dun point de vue applicatif, il est important de se rappeler que : T T mais que T < T Est-ce quune fonction récursive est meilleur quune solution itérative ?

118 117 Fonctions récursives La question importante : Comment arrêter une fonction récursive? Les cas limites sont des éléments essentiels de toutes fonctions récursives : –ce sont les conditions qui cessent les appels récursifs et permettent de terminer la fonction –ce sont des tests darrêt qui sont liés aux parties non récursives de la fonction Il existe deux types de récursivité : –la récursivité directe correspond au cas où la fonction F appel directement la fonction F –la récursivité indirecte correspond au cas où une fonction F appel F 1, qui appel F 2, qui appel F 3,..., qui appel F n, qui enfin appel la fonction F

119 118 Fonctions récursives Un premier exemple : La recherche dun mot dans le dictionnaire –la solution itérative, simple mais peu efficace, correspond à parcourir dans lordre, du premier vers le dernier, tous les éléments du dictionnaire jusquà ce que le mot recherché soit identifié –une solution plus efficace et plus élégante consiste à faire ce que nous faisons intuitivement : une recherche binaire (« binary search ») Comment procède-t-on pour résoudre ce problème?

120 119 Fonctions récursives Pseudo code dun algorithme de recherche binaire : Le tableau de données doit être trié sinon la recherche dichotomique nest pas applicable. A[0] A[1] A[2] A[3]... A[n] Fonction-Recherche-binaire (Dictionnaire, Mot) SI Dictionnaire est réduit à une seule page ALORS parcourir la page pour localiser Mot SINON Trouver la partie du Dictionnaire séparé par le milieu où se trouve Mot SI Mot se trouve dans la première partie du Dictionnaire Fonction-Recherche-Binaire( première partie de Dictionnaire, Mot) Sinon Fonction-Recherche-Binaire( deuxième partie de Dictionnaire, Mot)

121 120 Fonctions récursives Comment trouver une solution récursive? Réponses clés à trouver pour construire une solution récursive : –Est-ce que le problème peut être définie en terme de sous problème de même nature? –Est-ce que chaque appel récursif diminue la taille du problème? –Quel est le cas limite du problème? –Puisque la taille du problème diminue, est on assuré datteindre le cas limite?

122 121 Fonctions récursives Aussi, plusieurs algorithmes itératifs peuvent facilement être modifier en algorithmes récursifs Un exemple simple de transformation algorithmique : le parcours dune boucle void AfficheTousLesNombres2Chiffres_Iteratif(int APartirDuNombre) { for (int i = APartirDuNombre; i < 100; i++) { printf("%d\n", i); } void AfficheTousLesNombres2Chiffres_Recursif(int APartirDuNombre) { if (APartirDuNombre < 100) { printf("%d\n", APartirDuNombre); AfficheTousLesNombres_Recursif(APartirDuNombre + 1); }

123 122 Fonctions récursives Un exemple un peu plus étayé de transformation algorithmique : le parcours dune double boucle void InitDynamicArrayByCascade_Iterative(int **Array, int SizeX, int SizeY, int Value) { for (int iY = 0; iY < SizeY; iY++) { for (int iX = 0; iX < SizeX; iX++) { Array[iX][iY] = Value; } void InitDynamicArrayByCascade_Recursive(int **Array, int SizeX, int SizeY, int Value) { if (SizeY > 0) { if (SizeX > 0) { Array[SizeY - 1][SizeX - 1] = Value; InitDynamicArrayByCascade_Recursive(Array, SizeX - 1, SizeY, Value); } InitDynamicArrayByCascade_Recursive(Array, SizeX, SizeY – 1, Value); }

124 123 Fonctions récursives La méthode de représentation suivante permet de décrire simplement un problème récursif par lidentification systématique de toutes les tâches possibles à chaque « itération » : –lidée consiste à décrire tous les cas possibles du problème –il est important dexprimer les tâches à faire et la nature récursive des cas concernés –il est intéressant de voir que les cas limites apparaissent clairement avec cette méthode

125 124 Fonctions récursives Un autre exemple : le calcul de la factorielle. –La définition mathématique de la factorielle est connue et simple :

126 125 Fonctions récursives Suite de lexemple : le calcul de la factorielle Fonction-Factoriel (Nombre) SI Nombre < 0 retourne erreur Si Nombre = 0 retourne 1 SINON retourne Nombre * Fonction-Factoriel(Nombre – 1) int Factoriel(int N) { if (N < 0) {/* Parametre dentree invalide */ return -1;/* -1 represente erreur */ } else if (N == 0) { return 1;/* Cas limite */ } else { return N * Factoriel(N – 1);/* Appel recursif */ }

127 126 Fonctions récursives Puisque chaque appel de fonction possède son propre espace de variable, chaque appel récursif possède le sien. Ceci implique un parcours descendant jusquau dernier appel récursif pour ensuite revenir au premier appel. La technique de représentation par boîtes est très efficace pour illustrer le comportement dun programme récursif. Elle aide à : –déverminer les programmes récursifs (parfois difficiles à suivre) –illustrer le comportement dun algorithme

128 127 Fonctions récursives Les étapes à suivre pour réaliser la technique de représentation par boîtes : –pour chaque appel récursif (parcours descendant), on crée une boîte qui contient lappel de fonction incluant les valeurs de chaque paramètre lenvironnement local du sous programme associé les instructions faites avant lappel récursif une flèche initiant le nouvel appel récursif –pour chaque dernier appel récursif, on revient à lappel de fonction appelante en créant une boîte contenant : lenvironnement local du sous programme associé les instructions faites après lappel récursif un flèche indiquant le retour de fonction avec la valeur retournée sil y a lieu

129 128 Fonctions récursives Exemple de représentation par boîte : int main(void) { printf("%d! = %d\n", 3, Factoriel(3)); } 3! = 6 - aucune entrée - Entrée Sortie

130 129 Fonctions récursives Un exemple : écrire une chaîne de caractères à lenvers (1 ier ) void Writebackward1(char *String, int Length) { if (Length > 0) { printf("%c", String[Length - 1]); Writebackward1(String, Length – 1); } Writebackward1("allo", 4); olla - aucune entrée - Entrée Sortie

131 130 Fonctions récursives Un exemple : écrire une chaîne de caractères à lenvers (2 ième ) void Writebackward2(char *String) { if (String[0] != '\0') { Writebackward2(String + 1);/* équivalent à WB2(&String[0] */ printf("%c", String[0]); } Writebackward2("allo"); olla - aucune entrée - Entrée Sortie

132 131 Fonctions récursives Un exemple : impression selon un ordre particulier void ImpressionEtrange(int *Data, int N) { if (N > 0) { ImpressionEtrange(Data + 1, N – 1); printf("%d \n", *Data); ImpressionEtrange(Data + 1, N – 1); ) } int Data[3] = { 1, 2, 3 }; ImpressionEtrange(Data, 3); aucune entrée - Entrée Sortie

133 132 Fonctions récursives suite de lexemple à la page suivante...

134 133 Fonctions récursives... fin de lexemple de la page précédente

135 134 Fonctions récursives Comme le montre indirectement lexemple précédent, il peut être très pratique dutiliser laffichage de létat courant du programme avant et après chaque appel récursif. Cette pratique est performante pour aider à la compréhension dun algorithme et surtout au déverminage. void FonctionQuelconque(int *Data, int N) { if (N > 0) { printf("1 : %4d %4d \n", N, *Data); FonctionQuelconque(Data + 1, N – 1); printf("2 : %4d %4d \n", N, *Data); FonctionQuelconque(Data + 1, N – 1); printf("3 : %4d %4d \n", N, *Data); } int Data[3] = { 1, 2, 3 }; FonctionQuelconque(Data, 3); 1 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : aucune entrée - Entrée Sortie

136 135 Fonctions récursives Comme il arrive souvent en algorithmie, il est intéressant de faire certaines optimisations qui augmentent significativement le rendement des algorithmes réveloppés. Les fonctions récursives permettent certaines optimisations intéressantes.

137 136 Fonctions récursives Prenons pour exemple la fonction de puissance.

138 137 Fonctions récursives La fonction de puissance (suite) : version récursive Version itérative float PowerRec(float X, int Y) { if (Y == 0) { return 1.0f; } else if (Y < 0) { return 1.0f / PowerRec(X, -Y); } else { return X * PowerRec(X, Y – 1); } float PowerIter(float X, int Y) { float Result = 1.0f; for (int i = abs(Y); i > 0; i--) { Result *= X; } return (Y > 0) ? (Result) : (1.0f / Result); }

139 138 Fonctions récursives La fonction de puissance (suite) : version récursive optimisée Une simple manipulation mathématique nous permet de démontrer : float PowerRecOptimized(float X, int Y) { if (Y == 0) { return 1.0f; } else if (Y < 0) { return 1.0f / PowerRecOptimized(X, -Y); } else { if (Odd(Y)) { return X * Sqr(PowerRecOptimized(X, Y / 2)); } else { return Sqr(PowerRecOptimized(X, Y / 2)); }

140 139 Fonctions récursives Laquelle de ces trois fonctions est la plus performante et pourquoi? Lexemple des fonctions de puissance montre de façon convaincante lamélioration que certaines optimisations peuvent faire. Ce tableau montre un indice de performance pour lappel des trois fonctions montrées avec comme valeur dexposant FonctionNombre de parcours de boucle PowerRec10000 appels récursifs PowerIter10000 itérations PowerRecOptimized 14 appels récursifs (répartit en 5 appels impairs et 9 pairs)

141 140 Fonctions récursives On revient sur lexemple de recherche dichotomique. Fonction-Recherche-binaire (Dictionnaire, Mot) SI Dictionnaire est réduit à une seule page ALORS parcourir la page pour localiser Mot SINON Trouver la partie du Dictionnaire séparé par le milieu où se trouve Mot SI Mot se trouve dans la première partie du Dictionnaire Fonction-Recherche-Binaire( première partie de Dictionnaire, Mot) Sinon Fonction-Recherche-Binaire( deuxième partie de Dictionnaire, Mot) Individu* RechercheIndividu(Individu Individus[], char Nom[], int Premier, int Dernier) { if (Premier > Dernier) { return NULL; } else { int Milieu = (Premier + Dernier) / 2; int ResultatComparaison = strcmp(Individus[Milieu].Nom, Nom); if (ResultatComparaison == 0) { return &Individus[Milieu]; } else if (ResultatComparaison > 0) { return RechercheIndividu(Individus, Nom, Premier, Milieu – 1); } else { return RechercheIndividu(Individus, Nom, Milieu + 1, Dernier); }

142 141 Fonctions récursives void main(void) { Individu ListePersonnes[9], *PersonneCible; strcpy(ListePersonnes[0].Nom, "Aleksia");ListePersonnes[0].Age = 31; strcpy(ListePersonnes[1].Nom, "Frédérick");ListePersonnes[1].Age = 2; strcpy(ListePersonnes[2].Nom, "Karolanne");ListePersonnes[2].Age = 14; strcpy(ListePersonnes[3].Nom, "Kassandra");ListePersonnes[3].Age = 10; strcpy(ListePersonnes[4].Nom, "Kristian");ListePersonnes[4].Age = 34; strcpy(ListePersonnes[5].Nom, "Kzavier");ListePersonnes[5].Age = 49; strcpy(ListePersonnes[6].Nom, "Michaël");ListePersonnes[6].Age = 12; strcpy(ListePersonnes[7].Nom, "Nikolas");ListePersonnes[7].Age = 30; strcpy(ListePersonnes[8].Nom, "Patrick");ListePersonnes[8].Age = 32; PersonneCible = RechercheIndividu(ListePersonnes, "Frédérick", 0, 8); PersonneCible = RechercheIndividu(ListePersonnes, "Kroutchev", 0, 8); }

143 142 Fonctions récursives Encore un exemple : la suite de Fonacci Le rythme de reproduction des lapins est un phénomènes pouvant être représenté approximativement par cette suite. Quelle est lévolution du nombre de lapin dans le temps en faisant les suppositions suivantes : –les lapins ne meurent jamais –un lapin atteint sa maturité sexuelle 2 mois après sa naissance (cest-à-dire au début du 3 ième mois de vie) –au début de chaque mois, chaque paire mâle/femelle sexuellement mûre donne naissance exactement à une paire mâle/femelle –on commence par une paire mâle/femelle naissants

144 143 Fonctions récursives Suite de Fibonacci (suite) Mois Non mûre 1 ière semaine Non mûre 2 ième semaine MûreTotal

145 144 Fonctions récursives Suite de Fibonacci (suite) unsigned int FibonacciRec(unsigned int N) { if (N == 0) { return 0; } else if (N == 1) { return 1; } else { return FibonacciRec(N - 1) + FibonacciRec(N – 2); }

146 145 Fonctions récursives Suite de Fibonacci (suite) –Cette première version récursive, quoique mathématiquement élégante et juste, est si peu performante quelle devient pratiquement non fonctionnelle. Pour Fib(N), cette solution donne :Fib(N+1) – 1 additions 2 Fib(N+1) – 2 appels récursifs void main(void) { FibonacciRec(0);/* 0 appel récursif */ } void main(void) { FibonacciRec(1);/* 0 appel récursif */ } void main(void) { FibonacciRec(2);/* 2 appels récursifs */ } void main(void) { FibonacciRec(3);/* 4 appels récursifs */ } void main(void) { FibonacciRec(4);/* 8 appels récursifs */ } void main(void) { FibonacciRec(5);/* 14 appels récursifs */ } void main(void) { FibonacciRec(6);/* 24 appels récursifs */ } void main(void) { FibonacciRec(7);/* 40 appels récursifs */ }

147 146 Fonctions récursives Une simple optimisation est assez facile : Ces solutions donnent : N – 1 appels récursifs pour la version réc. optimisée Nitération pour la version itérative unsigned int FRO(unsigned int N, unsigned int Previous, unsigned int Current) { if (N <= 1) { return Current; } else { return FRO(N – 1, Current, Current + Previous); } unsigned int FibonacciRecOptimized(unsigned int N) { return (N == 0) ? (N) : (FRO(N, 0, 1)); } unsigned int FibonacciIter(unsigned int N) { unsigned int R1 = 0, R2 = 1, R = 0, i; for (i = 1; i <= N; i++) { R = R1 + R2;R2 = R1; R1 = R; } return R; }

148 147 Fonctions récursives Plusieurs fonction graphique utilisent la récursivité : –Remplissage dune région bornée –Manipulations de polygones –Génération de fractals –Affichage de géométries particulières –...

149 148 Fonctions récursives Que fait la fonction suivante : void CoolDrawing1(int X, int Y, int R) { DrawCircle(X, Y, R);/* fonction dessinant un cercle centré à (X, Y) et de rayon R */ if (R > 2) { CoolDrawing1(X + R / 2, Y, R / 2); CoolDrawing1(X – R / 2, Y, R / 2); }

150 149 Fonctions récursives Comment tracer cette figure (lignes et flèches) ? void CoolDrawing2(int PreviousLength, int Length, int MaxLength) { if (Length <= MaxLength) { DrawLineFromCurrentPositionAndDirection(Length); TurnCursorToLeft(90); DrawArrowFromCurrentPositionAndDirection(Length); CoolDrawing2(Length, PreviousLength + Length, Maxlength); } CoolDrawing2(0, 1, 21);

151 150 Impact du mécanisme dappel des fonctions sur les fonctions récursives Puisque le mécanisme dappel des fonctions sapplique également sur les fonctions récursives ou non, regardons quel est son impact sur lefficacité dune fonction. Prenons pour exemple la fonction suivante (servant à afficher un nombre en format binaire) : avec lappel de fonction suivant : void WriteBinary(int N) { if (N <= 1) { printf("%d", N); } else { WriteBinary(N / 2); printf("%d", N % 2); }... WriteBinary(6);...

152 151 Impact du mécanisme dappel des fonctions sur les fonctions récursives

153 152 Impact du mécanisme dappel des fonctions sur les fonctions récursives Le mécanisme dappel des fonctions génère deux impacts importants sur les fonctions récursives. En effet, lors de chaque appel : –une série dinstructions supplémentaires est générées. Ces instructions rendent lexécution de la fonction moins performante quune solution itérative équivalente. –un enregistrement dactivation est empilé sur la pile. Cet empilement requiert une quantité de mémoire non négligeable pour les fonctions récursives ayant un nombre dappel élevé. Il est relativement fréquent de voir une fonction mal conçue à ce niveau générer une erreur fatal du programme.

154 153 Pointeurs de fonctions En plus davoir des pointeurs faisant référence à des variables de divers types, le langage C permet la définition de pointeurs de fonction. Ces pointeurs servent à identifier ladresse dentrée de la première instruction dune fonction (point dentrée). On utilise souvent les pointeurs de fonction pour passer une fonction en argument à une autre fonction. La déclaration dun pointeur de fonction ressemble à la déclaration dun pointeur de type et à la déclaration dune fonction à la fois.

155 154 Pointeurs de fonctions La déclaration dun pointeur de fonction se fait par les parenthèses qui permettent de déterminer la variable pointeur: On vient de déclarer quatre pointeurs de fonction : –pFonction1 fait référence à une fonction ne retournant rien et ayant aucun paramètre dentrée –pFonction2 fait référence à une fonction retournant un entier et ayant deux entiers comme signature –pFonction3 fait référence à une fonction retournant un pointeur indéterminé et un pointeur indéterminé comme signature –pFonction4 fait référence à une fonction retournant un pointeur de fonction (ne retournant rien et nayant aucun paramètre dentrée) et ayant un pointeur de fonction (de même nature que pFonction2) et un entier comme signature. [ classe-mémorisation ] type-retour (*identificateur)( [ type1 param1,... ] ); void (*pFonction1)(void); int (*pFonction2)(int, int); void* (*pFonction3)(void*) (void (*)(void)) (*pFonction4)((int (*)(int, int)), int);

156 155 Pointeurs de fonctions Il faut faire attention à lambiguïté des déclarations dun pointeur de fonction et dune fonction retournant un pointeur. On vient de déclarer deux types complètement différents : –pFonction fait référence à un pointeur de fonction retournant un entier et ayant un entier comme paramètre dentrée –Fonction fait référence à une fonction retournant un pointeur dentier et ayant un entier comme paramètre dentrée On assigne la valeur dun pointeur de fonction en spécifiant ladresse dune fonction par : On lappel simplement comme une fonction standard par le pointeur : int (*pFonction)(int); int *Fonction(int); int Fonction(int N);... int (*pFonction)(int) = &Fonction; /* lopérateur dadressage (&) est facultatif */ int A = pFonction(N);

157 156 Pointeurs de fonctions Un premier exemple : int Resultat(int V1, int V2, int (*Compare)(int, int)) { return Compare(V1, V2); } int Max(int V1, int V2) { return (V1 >= V2) ? (V1) : (V2); } int Min(int V1, int V2) { return (V1 <= V2) ? (V1) : (V2); } void main(void) { printf("Min de 1 et 2 = %d\n", Resultat(1, 2, &Min)); printf("Max de 1 et 2 = %d\n", Resultat(1, 2, &Max)); } Min de 1 et 2 = 1 Max de 1 et 2 = 2 Min de 1 et 2 = 1 Max de 1 et 2 = Entrée Sortie

158 157 Pointeurs de fonctions Un deuxième exemple : int BrowseData(int *Data, int N, int (*ToDo)(int, int)) { int Resultat = *Data; for (int i = 0; i < N; i++) Resultat = ToDo(Resultat, Data[i]); return Resultat; } int Max(int V1, int V2) { return (V1 >= V2) ? (V1) : (V2); } int Min(int V1, int V2) { return (V1 <= V2) ? (V1) : (V2); } int Display(int V1, int V2) { return printf(" %d ", V2); } void main(void) { int Data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -9, -8, -7, -6, -5, -4, -3, -2, -1 }; printf("Min de toutes les données = %d\n", BrowseData(Data, 19, &Min)); printf("Max de toutes les données = %d\n", BrowseData(Data, 19, &Max)); printf("Affichage de toutes les données :\n"); BrowseData(Data, 19, &Display); } Min de toutes les donnÚes = -9 Max de toutes les donnÚes = 9 Affichage de toutes les données : Min de toutes les donnÚes = -9 Max de toutes les donnÚes = 9 Affichage de toutes les données : Entrée Sortie

159 158 Fonctions ellipsis Le C permet de déclarer des fonctions dont le nombre de paramètres est variable (les fonctions printf et scanf en sont de parfait exemple). Cette pratique est habituellement à proscrire en langage C++. Lun des points forts du langage C++ est de faire la vérification systématique de la compatibilité de tous les types, or les fonctions ellipsis ne permettent pas au compilateur de faire cette vérification de type. Malgré tout, cette technique est puissante et peut être très utile en langage C.

160 159 Fonctions ellipsis Cette technique est rendue possible grâce au fait que toutes les variables passées à une fonction se retrouvent tous empilées sur la pile lors de lappel de fonction. Avec la position initiale des paramètres en mémoire, le type et le nombre, il est assez aisé de les récupérer pour les utiliser. Toutes les fonctions ellipsis possède au moins un paramètre avant la déclaration ellipsis à proprement dite (...). Ce paramètre permet de connaître ladresse de départ de tout les paramètres de la fonction. Il sert souvent aussi pour déterminer le type et/ou le nombre de paramètre.

161 160 Fonctions ellipsis Limplémentation correcte de ce type de fonction requiert une grande précaution car plusieurs règles doivent être respectées : –il existe au moins un argument définit avant la déclaration ellipsis (...) –il faut faire attention aux « type promotions » pratiqués par les compilateurs –le corps dune fonction ellipsis doit être cohérent entre sa déclarations (incluant son comportement – extraction des données) et son utilisation –les macros va_arg, va_start, va_end servent à lextraction des paramètres

162 161 Fonctions ellipsis Un premier exemple (sans les macros) : void AfficheIntData(int N,...) { for (int i = 0; i < N; i++) { printf("%d ", *(&N + (1 + i))); } printf("\n"); } void AfficheDoubleData(int N,...) { for (int i = 0; i < N; i++) { printf("%0.1lf ", *(double*)((char*)&N + sizeof(int) + sizeof(double) * i)); } printf("\n"); } void main(void) { AfficheIntData(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); AfficheDoubleData(5, 1.9, 2.8, 3.7, 4.6, 5.5); } Entrée Sortie

163 162 Fonctions ellipsis Un deuxième exemple (avec les macros) : void AfficheIntData(int N,...) { va_list ParametreCourant; va_start(ParametreCourant, N); for (int i = 0; i < N; i++)printf("%d ", va_arg(ParametreCourant, int)); va_end(ParametreCourant); printf("\n"); } void AfficheDoubleData(int N,...) { va_list ParametreCourant; va_start(ParametreCourant, N); for (int i = 0; i < N; i++)printf("%0.1lf ", va_arg(ParametreCourant, double)); va_end(ParametreCourant); printf("\n"); } void main(void) { AfficheIntData(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); AfficheDoubleData(5, 1.9, 2.8, 3.7, 4.6, 5.5); } Entrée Sortie


Télécharger ppt "1 Convention sur les acétates Concernant la présentation des acétates : –malgré le fait quelles soient relativement bien remplies, les acétates tentent."

Présentations similaires


Annonces Google