• Aucun résultat trouvé

Pointeurs de fonctions

N/A
N/A
Protected

Academic year: 2022

Partager "Pointeurs de fonctions"

Copied!
41
0
0

Texte intégral

(1)

•  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);

(2)

Pointeurs de fonctions

•  Une fonction operateur_binaire prenant en paramètres deux entiers et une fonction de type int, qui prend elle- mê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.

(3)

•  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

(4)

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) ) ;

}

(5)

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

•  sil existe des parenthèses alors les éliminer

•  jusqu ’ à être ramené à un identificateur

(6)

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

(7)

•  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

(8)

Pointeurs

•  static char * c [ ] = { « MOINS », « PLUS », « EGAL »} ;

•  * c [ ] est un char

•  c [ ] pointeur vers char

•  c tableau de pointeurs vers char

(9)

•  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

(10)

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é.

(11)

• 

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.

(12)

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

(13)

•  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.

(14)

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);

(15)

#include « tab.h »

TAB creation(int taille) { malloc ...

return ...

}

void lecture(TAB t,int taille){

int i;

for (i=0;i<taille;i++) ...

}

...

(16)

Compilation séparée

fichier tri.h

/*module de tris de tableaux*/

// pour connaitre le type TAB

#include « tab.h »

void tri_bulle(TAB,int);

void tri_rapide(TAB,int);

fichier tri.c

#include « tri.h »

void tri_bulle(TAB t,int taille){

...

}

void tri_rapide tri_bulle(TAB t,int taille){

...

}

(17)

fichier main.c

#include « tab.h »

#include « tri.h » TAB montab;

int main( ){

montab=creation(10);

lecture(montab,10);

tri(montab,10);

affichage(montab,10);

}

(18)

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

(19)

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.

(20)

Compilation séparée

•   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).

(21)

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

(22)

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

(23)

•  On peut rajouter dans le makefile une ligne pour effacer les fichiers objets

clean :

rm –f *.o

(24)

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)

(25)

•  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)

(26)

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

(27)

•  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.

(28)

Macro avec arguments (Macro- instruction)

•  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)

(29)

•  Code original:

#define MIN(X,Y) ( (X)<(Y) ? (X) : (Y) )

….

MIN(a,b);

•  Code intermédiaire:

….

( (a) < (b) ? (a) : (b) )

(30)

Macros prédéfinies

•  Dans stdio.h

•  #define getchar() getc(stdin)

•  #define putchar(c) putc(c,stdout)

(31)

#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)

(32)

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;}

}

(33)

•  __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__);

(34)

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``);

(35)

•  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!!!!

(36)

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.

(37)

•  Écrire une macro qui calcule le maximum de deux

nombres et écrire un programme de test qui l'utilise

(38)

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));

•  }

(39)

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.

(40)

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.

(41)

#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

Références

Documents relatifs

b- Etape 2 : Testez votre module à l’aide d’un programme qui fera appel à ces fonctions à plusieurs reprises, avec des arguments variés pour dessiner une série de carrés et

N’oubliez pas d’enlever le petit morceau pointu à mettre dans la poubelle dans un papier pour ne pas vous blesser en sortant votre sac-poubelle. (note 2) Tester vos mélanges de

Du temps pour arriver au bout mais vous serez si content de vous servir de votre petit pot, bien sûr en pâtisserie mais aussi en cuisine “salée” par exemple

a+i c’est l’adresse du i-ème élément du tableau a+i c’est aussi &amp;a[i]. L’incrément/décrément dépend du type de

O(^) étant une fonction rationnelle à coefficients entiers, soit [JL le nombre de valeurs distinctes qu'elle prend lorsqu'on remplacer successivement par les m racines d'une

1.. voir mon article Expression asympto- tique de certaines fonctions entières {Nouvelles Annales),.. Fonctions entières de genre un.. Fonctions de genre deux.. Généralisation

On règle ainsi la valeur de départ, le pas (par exemple le pas vaut 1 si on veut calculer les valeurs de 1 en 1, 2 si ou veut les calculer de 2 en 2...) et éventuellement la valeur

• Fonction lecture_fic(p_nom) qui prend en paramètre une chaîne de caractères correspon- dant à un nom de fichier, charge le contenu de ce fichier texte (représentation