Florence Levé - Université de Picardie Jules Verne 145 Pointeurs de fonctions Parfois utile de passer une fonction comme paramètre d une autre fonction Un pointeur de fonction correspond à l adresse du début du code de la fonction Un pointeur sur une fonction de prototype type fonction (type_1,..., type_n); est de type type (*)(type_1,..., type_n);
Florence Levé - Université de Picardie Jules Verne 146 Pointeurs de fonctions Une fonction operateur_binaire prenant en paramètres deux entiers et une fonction de type int, qui prend ellemême 2 entiers en paramètres, sera définie par : int operateur_binaire (int, int, int(*f)(int, int)) Sa déclaration est donnée par int operateur_binaire (int, int, int(*)(int, int)); Pour appeler la fonction operateur_binaire, on utilisera comme troisième paramètre l identificateur de la fonction utilisée.
Florence Levé - Université de Picardie Jules Verne 147 Pointeurs de fonctions Par exemple, si somme est une fonction de prototype int somme(int, int); on appelle operateur_binaire pour la fonction somme par l expression : operateur_binaire(a,b,somme); Remarque : on n utilise pas la notation &somme comme paramètre effectif de operateur_binaire
Florence Levé - Université de Picardie Jules Verne 148 Pointeurs de fonctions Pour appeler la fonction passée en paramètre dans le corps de la fonction operateur_binaire, on écrit (*f )( a, b). Exemple int operateur_binaire (int a, int b, int (*f) (int, int)) { } return ( (*f ) (a,b) ) ;
Florence Levé - Université de Picardie Jules Verne 149 Pointeurs Interprétation de déclarations pouvant paraître complexes int (*(*f [ ] ) ( ) ) ; /*???????????????????/* Algorithme Répéter s il existe * d indirection alors le traiter et l éliminer sinon s il existe [] de tableau alors le traiter et l éliminer sinon s il existe des parenthèses alors les éliminer jusqu à être ramené à un identificateur
Florence Levé - Université de Picardie Jules Verne 150 Pointeurs char * f ( ) ; * f ( ) est un char f ( ) un pointeur vers char f une fonction retournant un pointeur vers un char int * f [10] ; *f [10] est un entier f [10] un pointeur vers un entier f un tableau (de 10 éléments) de pointeurs vers un entier
Florence Levé - Université de Picardie Jules Verne 151 Pointeurs char (*f ) ( ); (*f ) ( ) est un char (*f ) une fonction qui retourne un char *f une fonction qui retourne un char f un pointeur vers une fonction retournant un char char *( *f ) ( ); *( *f ) ( ) est un char (*f ) ( ) un pointeur vers un char (*f ) une fonction retournant un pointeur vers un char *f une fonction retournant un pointeur vers un char f un pointeur vers une fonction retournant un pointeur vers un char
Florence Levé - Université de Picardie Jules Verne 152 Pointeurs static char * c [ ] = { «MOINS», «PLUS», «EGAL»} ; * c [ ] est un char c [ ] pointeur vers char c tableau de pointeurs vers char
Florence Levé - Université de Picardie Jules Verne 153 Exercice TABLEAU DE FONCTIONS Ecrivez un programme calfon permettant d obtenir à la console des valeurs des fonctions : sin, cos, exp, log. Exemple : $ calfon sin 0.5 0.479426 $ calfon log10 23 log10 : fonction inconnue
Florence Levé - Université de Picardie Jules Verne 154 Compilation séparée Modularité Un programme doit être découpé en plusieurs fichiers, même de petites tailles (réutilisabilité, lisibilité, etc.). Chaque composante logique (un module) regroupe les fonctions et types autour d'un même thème. Les modules s'appellent les uns les autres. Si M1 utilise une fonction de M2, il n'a pas besoin du code de la fonction mais juste de sa signature, c'est à dire son nom, le nombre de paramètres, leur ordre et leur type, et le type retourné.
Florence Levé - Université de Picardie Jules Verne 155 Compilation séparée Remarque : si le compilateur rencontre un appel à une fonction qu'il ne connaît pas (qui n'a pas été déclarée précédemment), il ne râle pas et considère par défaut que c'est une fonction de type int. Il ne se mettra à râler que si il rencontre plus tard une définition contradictoire pour cette fonction on lui demande de faire avec cette fonction quelque chose qu'on ne peut pas faire avec un int S'il ne trouve nulle part le code de cette fonction, c'est lors de l'édition des liens qu'il râlera.
Florence Levé - Université de Picardie Jules Verne 156 Compilation séparée Pour chaque module truc, on fera 2 fichiers fichier entête truc.h. L'interface. Contient la signature de toutes les fonctions exportées et la déclaration des types exportés. fichier truc.c. Le code de toutes les fonctions exportées, déclaration de variables locales, fonctions locales, types locaux. Et contient au début l'inclusion du.h (pour vérifier qu'on raconte bien la même chose partout) Tout module ou programme principal qui a besoin des fonctions du module truc, devra juste inclure le fichier truc.h
Florence Levé - Université de Picardie Jules Verne 157 Compilation séparée Exemple : module tab de manipulation de tableaux module tri qui offre plusieurs méthodes de tris de tableaux un programme principal qui veut créer un tableau, le trier par le tri bulle et l'afficher.
Florence Levé - Université de Picardie Jules Verne 158 Compilation séparée fichier tab.h /* module de gestion de tableaux */ typedef int *TAB; // type TAB = un tableau void lecture(tab,int); // fonction de lecture d'un tableau de taille donnee //... commentaire sur ce que fait chaque fonction void affichage(tab,int); TAB creation(int); int acces(tab,int);
Florence Levé - Université de Picardie Jules Verne 159 Compilation séparée #include «tab.h» fichier tab.c TAB creation(int taille) { malloc... return... } void lecture(tab t,int taille){ int i; for (i=0;i<taille;i++)... }...
Florence Levé - Université de Picardie Jules Verne 160 Compilation séparée fichier tri.h fichier tri.c /*module de tris de tableaux*/ #include «tri.h» // pour connaitre le type TAB #include «tab.h» void tri_bulle(tab,int); void tri_rapide(tab,int); void tri_bulle(tab t,int taille){... } void tri_rapide tri_bulle(tab t,int taille){... }
Florence Levé - Université de Picardie Jules Verne 161 Compilation séparée #include «tab.h» #include «tri.h» fichier main.c TAB montab; int main( ){ montab=creation(10); lecture(montab,10); tri(montab,10); affichage(montab,10); }
Florence Levé - Université de Picardie Jules Verne 162 Compilation séparée Comment relier tout ça? avec tab.c et tab.h, on forme un fichier objet tab.o avec tri.c, tab.h et tri.h, on forme un fichier objet tri.o avec main.c, tri.h et tab.h, on forme un fichier objet main.o On fait l'édition des liens des main.o tri.o et tab.o et on obtient un éxecutable. C'est à dire qu'il faut donner les ordres de compilation : gcc -c tab.c gcc -c tri.c gcc -c main.c gcc tab.o tri.o main.o -o prog
Florence Levé - Université de Picardie Jules Verne 163 Compilation séparée - Makefile fichier Makefile MonProg: main.o tab.o tri.o <tab> gcc main.o tab.o tri.o -o MonProg main.o : main.c tab.h tri.h <tab> gcc -c main.c tri.o : tri.c tri.h tab.h <tab> gcc -c tri.c tab.o : tab.c tab.h <tab> gcc -c tab.c Taper make pour l'exécuter.
Compilation séparée Florence Levé - Université de Picardie Jules Verne 164 Intérêt : indispensable quand il y a beaucoup de fichiers (plutôt que de retaper à chaque fois tous les ordres de compilation) il ne fait que ce qui est nécessaire : il ne recompile quelque chose que si la dernière compilation est plus ancienne que la dernière modification d'un fichier (c'est pourquoi il est indispensable de mettre tous les.h utilisés).
Florence Levé - Université de Picardie Jules Verne 165 Compilation séparée Raccourcis On peut déclarer des variables VAR=machins qu'on utilise ensuite par $(VAR). La maintenance et la mise à jour sont alors plus faciles à gérer. fichier Makefile CC = gcc OBJ = main.o tab.o tri.o MonProg: $(OBJ) $(CC) $(OBJ) -o MonProg main.o : main.c tab.h tri.h tri.o : tri.c tri.h tab.h tab.o : tab.c tab.h
Florence Levé - Université de Picardie Jules Verne 166 Compilation séparée Utilisation de macros spéciales : $@ nom de la cible à reconstruire $* nom de la cible sans suffixe $< nom de la dépendance à partir de laquelle on reconstruit la cible $? liste des dépendances plus récentes que la cible
Florence Levé - Université de Picardie Jules Verne 167 Compilation séparée On peut rajouter dans le makefile une ligne pour effacer les fichiers objets clean : rm f *.o
Florence Levé - Université de Picardie Jules Verne 168 Préprocesseur Qu est-ce que c est? Simple substitution de texte Augmente la lisibilité du code source Augmente la vitesse d exécution du programme Le preprocessing intervient avant la phase de compilation Directives Une directive au préprocesseur commence toujours par # #include, #define, #ifdef Les directives sont effectives sur tout le fichier. Remplacement des directives de précompilation par du code en C (compréhensible par le compilateur)
Florence Levé - Université de Picardie Jules Verne 169 Macros Définies par la directive #define Deux formes: Simple remplacement d une chaîne de caractère. Remplacement de la chaîne et substitution d argument. Une macro peut être définie sur plusieurs lignes (retour chariot)
Florence Levé - Université de Picardie Jules Verne 170 Macro simple #define IDENTIFIANT chaîne chaîne est une séquence de caractères Toute occurrence de IDENTIFIANT dans le texte sera remplacé par chaîne Exemple : #define PI!3.14159! Remplacera durant la phase de préprocessing toutes les occurrences de la chaîne «PI» dans le fichier C par la chaîne «3.14159». Autre forme de déclaration de constantes
Florence Levé - Université de Picardie Jules Verne 171 Macro simple On peut définir les variables de précompilation à l'appel de l'enchaîneur de passes option Dnom=valeur Le préprocesseur changera les occurrences de la variable nom par la valeur valeur, comme si la constante de précompilation nom était définie par la directive #define nom valeur.
Florence Levé - Université de Picardie Jules Verne 172 Macro avec arguments (Macroinstruction) Syntaxe #define <chaîne1>(<liste paramètres>) <chaîne2>! (pas d espace entre l identifiant et la parenthèse)!! Exemple Macro qui incrémente une variable #define inc(x) x++! Toute occurrence de «inc(<variable>)» sera respectivement remplacée par <variable>++ durant la phase de précompilation (indépendamment de son type)
Florence Levé - Université de Picardie Jules Verne 173 Macro à arguments multiples Code original: #define MIN(X,Y) ( (X)<(Y)? (X) : (Y) ). MIN(a,b); Code intermédiaire:. ( (a) < (b)? (a) : (b) )
Florence Levé - Université de Picardie Jules Verne 174 Macros prédéfinies Dans stdio.h #define getchar() getc(stdin) #define putchar(c) putc(c,stdout)
Florence Levé - Université de Picardie Jules Verne 175 Problèmes courants Code original: #define SQR(X) X*X SQR(a+1); Code intermédiaire: a+1*a+1; //ce qui est égal à a+(1*a)+1 Solution: parenthéser: #define SQR(X) (X)*(X)
Florence Levé - Université de Picardie Jules Verne 176 Les macros ne sont pas des fonctions Code original: #define SWAP(X,Y) {int temp; temp=x; X=Y; Y=temp;} void main(int){ int a=3, b=4; SWAP(a,b); } Code intermédiaire: void main(void){ int a=3,b=4; {int temp; temp=a; a=b; b=temp;} }
Florence Levé - Université de Picardie Jules Verne 177 Macro prédéfinies FILE : nom du fichier source courant LINE : numéro de ligne courant DATE : date de compilation du fichier courant TIME : heure de compilation du fichier courant printf(``%s a été compilé le %s\n``, FILE, DATE );
Florence Levé - Université de Picardie Jules Verne 178 Opérateurs de Macro # permet de convertir l argument en chaîne de caractères #define MSG(F) printf(#f) MSG(test mess); //devient printf(«test mess»); ## permet de concaténer #define ERR(X,Y) printf(x ## Y) ERR(``err: ``,``une erreur``); //devient printf(``err: une erreur``);
Florence Levé - Université de Picardie Jules Verne 179 Dédéfinir et redéfinir des macros Il n est pas permis de redéfinir des macros, sauf si elles ont été dédéfinies par #undef au préalable. #undef est ignoré si l identifiant n a pas été défini #undef peut dédéfinir des macro prédéfinies!!!!
Florence Levé - Université de Picardie Jules Verne 180 Inclure des fichiers On inclut des fichiers textes par #include Le texte du fichier est alors recopié à cet endroit. #include <stdio.h> cherche dans le chemin standard le fichier stdio.h #include ``monfichier.h`` cherche le fichier monfichier.h dans le répertoire courant.
Florence Levé - Université de Picardie Jules Verne 181 Exercices Écrire une macro qui calcule le maximum de deux nombres et écrire un programme de test qui l'utilise
Florence Levé - Université de Picardie Jules Verne 182 Solution #include <stdio.h> /* Definition de la macro */ #define max(x,y) (((x) > (y))? (x) : (y)) /* Le programme de test */ int main ( ) { int n1, n2; /* Deux nombres a fournir par l'utilisateur */ /* Demande et lecture des deux nombres */ printf("introduire n1 : "); scanf("%d", &n1); printf("introduire n2 : "); scanf("%d", &n2); /* Calcul du maximum et affichage du resultat */ printf("max(%d, %d) = %d\n", n1, n2, max(n1, n2)); }
Florence Levé - Université de Picardie Jules Verne 183 Inclure des fichiers Possible de demander au pré-processeur d'ajouter d'autres catalogues à sa recherche option de compilation -Inom_du_catalogue. Cette option peut être utilisée plusieurs fois de manière à spécifier plusieurs catalogues de recherche. Les fichiers inclus peuvent contenir d'autres inclusions de fichiers. Ce processus récursif est parfois limité à quelques niveaux d'inclusion.
Florence Levé - Université de Picardie Jules Verne 184 Inclure des fichiers Si le nom du fichier est entre guillemets, le préprocesseur cherche le fichier : dans le catalogue courant, puis dans les catalogues spécifiés par les options -I. Si le nom du fichier est entre < >, le préprocesseur cherche le fichier : dans les catalogues spécifiés par les options -I, puis dans le catalogue par défaut du compilateur.
Florence Levé - Université de Picardie Jules Verne 185 Conséquence des inclusions #include <stdio.h> typedef int *p_int; #include ``prog1.h`` int main(void){ } Prog1.c pourra alors utiliser les fonctions de la bibliothèque stdio.h