LA RÉCURSIVITÉ Année Académique: 2010-2011 1/199 Introduction L'un des principes fondamentaux en informatique est la récursivité. Cette technique permet une représentation plus compacte et plus simples des algorithmes. Année Académique: 2010-2011 2/199 Définition La récursivité est le principe de définition d'un problème, d'une fonction ou d'une méthode en fonction de lui-même. Les définitions récursives sont en général plus courtes et plus faciles que d'autres présentations, puisqu'elles soulignent les propriétés caractéristique d'une fonction. Année Académique: 2010-2011 3/199 Définition Une fonction récursive f est définie pour partie en référence à elle-même. Sa définition est une structure conditionnelle comprenant : ● Un test d'arrêt qui détecte un sous-ensemble de valeurs vi du domaine de définition de la fonction et les fait correspondre à des définitions non récursives de la fonction ● Des fonctions qui provoquent la convergence des valeurs de la variable de récursion vers des valeurs satisfaisant l'un des tests d'arrêt de la fonction. 4/199 Année Académique: 2010-2011 Définition L'importance de la récursivité réside dans la possibilité de décrire dans une proposition finie un ensemble d'objets. De la même manière un ensemble infini de calcul peut se laisser décrire par un programme récursif. Dans la programmation impérative, un outil suffisant et nécessaire pour présenter les programmes récursifs est la fonction (sousprogramme) Année Académique: 2010-2011 5/199 Quand utiliser la récursion ? Le problème de départ doit se laisser partitionner en des exemplaires plus simples du même problème. ● Quand tous les sous-problèmes sont résolus, les solutions doivent se laisser fusionner de telle manière que la solution du problème initial soit établit. ● Année Académique: 2010-2011 6/199 Quand utiliser la récursion ? ● Un grand problème doit se laisser partitionner en des sous-problèmes moins complexes et plus faciles tels qu'ils peuvent se laisser résoudre sans autre partition. Année Académique: 2010-2011 7/199 Exemples Additionner deux entiers. Add(X, 0) = X Add(X,Y+1) = 1+Add(X,Y) Multiplier deux entiers Mult(X,0) = 0 Mult(X,Y+1) = X+Mult(X,Y) Année Académique: 2010-2011 8/199 La puissance de la récursivité La puissance la récursivité se trouve dans la possibilité de définir un ensemble d'objets infinis grâce à un nombre fini d'expressions. On peut aussi définir un nombre infini de traitements en utilisant un nombre fini d'instructions. Année Académique: 2010-2011 9/199 Usage La récursivité est particulièrement adaptée quand le problème à résoudre, la fonction à calculer ou bien la structure de données à déclarer peut se décrire en termes récursives. Année Académique: 2010-2011 10/199 Dangers Comme avec les structures itératives (boucles), la récursivité peut introduire la possibilité de la non terminaison. Il est donc essentiel d'examiner la terminaison des calculs récursifs. Une condition fondamentale est qu'un appel récursif soit soumis à une condition qui peut devenir fausse à un moment donné. Année Académique: 2010-2011 11/199 Dangers Dans la pratique, il n'est suffisant de montrer qu'un algorithme récursif se termine, c'est à dire le nombre d'appels récursifs est fini, mais il faut aussi démontrer que ce nombre n'est pas très grand. Ceci parce qu'un appel récursif implique un surcoût. Année Académique: 2010-2011 12/199 Schéma général Un appel récursif se présente sous l'une des formes suivantes : ● P = if B P[S,P] ● P = P[S, if B P] Année Académique: 2010-2011 13/199 Quand ne pas utiliser la récurrence? Les solutions récursives ne sont pas appropriés pour des problèmes qui respectent les formes suivantes : ● P = if B S ; P ● P = S ; if B P Dans ces schéma, on fait appel à P une seule fois : soit au début soit à la fin. Année Académique: 2010-2011 14/199 Exemple La fonction factorielle On a : f(0)=1 f(n+1)=(n+1)*f(n) Si on ajoute deux variables I et F pour contenir n et f(n) correspondant, on a : P= if I<n {I=I+1 ; F=I*F ; P} Année Académique: 2010-2011 15/199 Exemple On a la fonction suivante : int factorielle (int n){ if (n>0) return n*factorielle(n-1) ; else return 1 ; } Année Académique: 2010-2011 16/199 Exemple La fonction itérative correspondante est : int factorielle (int n){ int i=0 ; int f=1 ; while (i<n) f*=++i ; return f ; } Année Académique: 2010-2011 17/199 Les algorithmes récursifs La récursivité peut se présenter d'au moins deux manières : ● La récursivité directe : P appelle P ● La récursivité indirecte : P appelle Q et Q appelle P Ainsi la récurrence peut ne pas être apparente quand on a un algorithme. Année Académique: 2010-2011 18/199 Les algorithmes récursifs Étudions les instructions de la forme : while B A. Désignons par V l'ensemble des variables, le schéma peut se réécrire : V=Vo ; while p(V) V=f(V) Avec P un prédicat et f une fonction. Année Académique: 2010-2011 19/199 Les algorithmes récursifs Si on désigne par vi la valeur de V après i passage dans la boucle. On a donc les conditions suivantes : v i= f v i− 1 , ∀ i0 v i≠ v j , ∀ i , j avec i≠ j pv n = false pv i = true , ∀ i , i n Année Académique: 2010-2011 20/199 Les suites composées La forme while B A est aussi appropriée pour le calcul des suites à termes composés. On veut calculer : Si on a s 0 = t 0t 1..t i On peuttdéfinir t i− 1les relations suivantes : i= f s i= s i− 1t i s0 = t 0 Année Académique: 2010-2011 21/199 La fonction pgcd La fonction pgcd peut être définie en utilisant les formules suivantes : ● pgcd (n,n)=n ● pgcd (m,n) = pgcd(n,m) ● pgcd(m,n) = pgcd(m,n-m) pour n>m Année Académique: 2010-2011 22/199 La fonction d'Ackermann Un exemple de fonction récursive est la fonction d'Ackermann. Elle est définie de la manière suivante : ● A(0,n) = n+1 ● A(m+1,0) = A(m,0) ● A(m+1,n+1) = A(m, A(m+1,n)) Année Académique: 2010-2011 23/199 Les structures de données récursives La définition d'un type est la spécification de l'ensemble des valeurs d'un domaine. On a des types statiques et des types dynamiques. Les types de données récursifs sont des types qui se définissent en termes d'eux même. Exemples : le type des expressions L'arbre généalogique. Année Académique: 2010-2011 24/199 Les structures de données récursives La propriété fondamentale des structures de données récursives (dynamiques) est que leurs tailles varient en fonction de la demande. Ainsi, on n'alloue pas une quantité fixée de mémoire à ces structures de données. La technique que l'on utilise pour réaliser cette allocation dynamique est l'utilisation des pointeurs. Année Académique: 2010-2011 25/199 Les pointeurs En C, on utilise l'opérateur * pour déclarer un pointeur. Ainsi int * p définit une variable p qui contient l'adresse d'une variable qui contient des données de type entier. Avec les pointeurs, on peut aussi allouer dynamiquement de la mémoire en utilisant l'instruction malloc de la bibliothèque stdlib.h. Année Académique: 2010-2011 26/199 Les listes linéaires - revisitées Une liste linéaire est une structure de données donc les données sont reliées entre elles. Dans la configuration la plus simple, un élément de la liste linéaire à une référence sur son successeur dans la liste. Année Académique: 2010-2011 27/199 Les listes linéaires Avec les listes linéaires, on a les constructeurs : ● createList qui crée une liste vide ● cons qui permet d'ajouter un élément à une liste. On a aussi les opérations suivantes : ● head qui donne l'élément de tête ● tail qui renvoie le reste la liste sans l'élément de tête ● length qui donne le nombre d'éléments de la liste ● empty qui dit si la liste est vide. Année Académique: 2010-2011 28/199 Les listes linéaires On a l'implémentation suivante (pour une liste linéaires de noms) : struct node{ char* nom; struct node* next; }; Année Académique: 2010-2011 29/199 Implémentations struct node* createList(void){ struct node* result=0 ; return result } Année Académique: 2010-2011 30/199 Implémentations struct node* cons(struct node * data, char* nom){ if (data==NULL){ data=(struct node*) malloc(sizeof(struct node)) ; data->nom=nom ; data->next=NULL ; }else{ data->next=cons(data->next, nom) ; } return data ; } Année Académique: 2010-2011 31/199 Implémentations char* head (struct node* data){ if (data==NULL) return NULL ; return data->nom ; } struct node* tail(struct node* data){ if (data==NULL) return NULL ; return data->next ; } Année Académique: 2010-2011 32/199 Implémentations int length(struct node* data){ if (data==NULL) return 0 ; return 1+length(data->next) ; } int empty(struct node* data){ return (data==NULL) ; } Année Académique: 2010-2011 33/199 Les structures arborescentes La récursion peut être utilisée pour structurer la séquence et l'itération. Les séquences et les itérations sont si communes que l'on les considère comme des structures fondamentales. Mais il existe des structures qui ne peuvent pas se laisser représenter en termes de séquence ni d'itération. Les structures arborescentes sont un exemple. Année Académique: 2010-2011 34/199 Définition Un arbre binaire est une structure de données constituée : ● une racine ● deux sous arbres (gauche et droit). Un arbre ordonné est un arbre donc les branches sont ordonnées. Un nœud y qui se trouve directement sous un nœud x est s’appelle successeur (descendant) de x. Si x se trouve au niveau i alors y sera au niveau i+1. x est appelé prédécesseur (ancestor) de y. Année Académique: 2010-2011 35/199 Définition La racine d’un arbre se trouve par définition au niveau 0. Le plus grand niveau d’un arbre s’appelle hauteur. Quand un élément n’a plus de successeur, l’on le désigne de feuille (leaf). Un élément qui n’est pas terminal s’appelle nœud interne. Le nombre de successeurs directs d’un nœud s’appelle grade. Le plus grand grade parmi tous les nœuds est le grade de l’arbre. Année Académique: 2010-2011 36/199 Définition Un arbre binaire est dit complet si tous les nœuds internes ont deux fils. Un arbre binaire de recherche est un arbre tel que tous les éléments qui sont à la partie gauche de l'arbre sont inférieurs au nœud interne et ceux de droite sont supérieurs. Un arbre AVL (nommé selon les mathématiciens russes Adelson-Velski et Landis qui les ont inventés en 1962) est un arbre binaire tel que la différence des hauteurs des deux sous-arbres ne dépasse pas 1. Année Académique: 2010-2011 37/199 Implémentation Un arbre binaire peut être défini de la manière suivante : struct binaryTree{ char* root; struct binaryTree* left ; struct binaryTree* right; } Année Académique: 2010-2011 38/199 Le backtracking La but d'un algorithme est d'être général. Il doit résoudre une classe de problème. Il existe des algorithmes qui pour résoudre le problème ne suivent pas de règles bien fixées. Au contraire, ils procèdent par des essais et erreurs (trial and errors). Il s'agit d'une classe d'algorithmes appelés algorithmes exploratifs. Ils explorent une ensemble fini d'états à la recherche de la solution. Année Académique: 2010-2011 39/199 Le backtracking En général, le problème à résoudre est subdivisé en sous tâches. Ces sous tâches se définissent en général plus facilement de manière récursive. Lors de l'exploration, si l'on se rend compte que ne va pas atteindre la solution, on recommence l'exploration à partir d'un autre état (backtracking). Année Académique: 2010-2011 40/199 Le problème des huit reines Nous allons présenter le problème huit reines pour essayer d'illustrer cette approche. Le problème consiste à placer huit reines sur un échiquier de telle sorte qu'aucune reine ne soit en danger. Ce problème a été traité par Gauss dans les années 1850. Année Académique: 2010-2011 41/199 Le problème des huit reines On a la procédure générique suivante : Try(i: INTEGER){ Initialiser les positions pour la ième reine. Repeter Faire un choix Si sécurise mettreReine ; Si i < 8 alors Try(i+1); Si non success alors enleverReine ; Jusqu'à succes ou plus de positions. Année Académique: 2010-2011 42/199 Diviser pour régner Une des techniques les plus importantes et les plus répandues de conception d'algorithmes est la stratégie appelée "diviser pour régner". Le principe en est de diviser un problème complexe de taille N en sous-problèmes plus petits et plus simples de manière que la solution de chaque sousproblème facilite la construction de la solution du problème entier. Nous allons utiliser les Tours d'Hanoï pour l'illustrer Année Académique: 2010-2011 43/199 Les Tours d'Hanoï On donne trois pieus (piquets ou pylônes) et un certain nombre N de disques de diamètres différents avec un trou central qui permet d'enfiler les disques sur les pieus. Initialement, les disques sont tous empilés sur le pieu P1 par diamètres décroissants en partant du bas. Le but du problème est de passer en un minimum de déplacements ces N disques du pieu P1 sur le pieu P2 en s'aidant du pieu P3 qui peut servir à des stockages intermédiaires de disques. Année Académique: 2010-2011 44/199 Les Tours d'Hanoï Les règles suivantes doivent être respectées: ● on ne peut déplacer qu'un seul disque à la fois, ● un disque peut être déplacé d'un des trois pieus sur l'un des deux autres, ● un disque ne peut être placé que sur le sol ou sur un disque de diamètre supérieur. On demande d'afficher tous les mouvements de disques qui doivent être effectués. Année Académique: 2010-2011 45/199 Les Tours d'Hanoï 1 2 Année Académique: 2010-2011 3 46/199 Les Tours d'Hanoï Pour déplacer les N disques du pieu 1 au pieu 3 (nous dirons plus simplement "de 1 à 3") en respectant les règles, on peut décomposer la tâche en trois parties: • déplacer les (N-1) premiers disques de 1 à 3 en respectant les contraintes, ● déplacer le N-ième disque de 1 à 2, • déplacer les (N-1 ) disques de 3 à 2 en respectant les contraintes. Année Académique: 2010-2011 47/199 Exemple – la suite de Fibonacci La suite de Fibonacci est définit de la manière suivante : F(0)=1 F(1)=1 F(n+2)=F(n+1)+F(n) Année Académique: 2010-2011 48/199 LES POINTEURS ET LA PROGRAMMATION DYNAMIQUE Année Académique: 2010-2011 49/199 Introduction «... Les pointeurs étaient mis dans le même sac que l'instruction goto comme une excellente technique de formuler des programmes incompréhensibles. Ceci est certainement vrai si les pointeurs sont employés négligemment, et on peut facilement créer des pointeurs qui pointent 'n'importe où'. Avec une certaine discipline, les pointeurs peuvent aussi être utilisés pour programmer de façon claire et simple. C'est précisément cet aspect que nous voulons faire ressortir dans la suite. ... » Kernighan & Ritchie dans leur livre 'Programming in C' Année Académique: 2010-2011 50/199 Introduction Les pointeurs sont tellement utilisé en C, parce que d'une part il s'agit souvent du seul moyen pour exprimer un calcul et d'autre part ils fournissent un moyen d'avoir des codes plus compacts et plus efficaces. Malheureusement les pointeurs (lorsque mal implémentés) sont à l'origine de nombreux bugs dans les programmes et du fameux message «segmentation fault (Erreur de segmentation)» Année Académique: 2010-2011 51/199 Adressage et variables La mémoire d'un ordinateur est constituée d'un ensemble contiguë de cellules appelées mots. Les mots peuvent être manipulés individuellement ou en groupe. Une variable est un nom donné à un mot ou à un groupe de mot (dépendant du type de la variable). Chaque mot à une adresse unique sur une longueur définie. Année Académique: 2010-2011 52/199 Les pointeurs Les pointeurs sont des variables qui contiennent les adresses d'autres variables. L'opérateur unaire & permet de connaître l'adresse d'une variable. Ainsi : int x ; &x donne l'adresse de la variable x. L'opérateur & s'applique uniquement aux valeurs en mémoire. Il ne peut donc être appliqué à une expression ou une constante. Année Académique: 2010-2011 53/199 Les pointeurs L'opérateur unaire * est un opérateur d'indirection ou de contenu. Quand il est appliqué à un pointeur, il produit en résultat la valeur pointé par le pointeur. Cet opérateur permet également de déclarer une variable comme étant de type pointeur. Année Académique: 2010-2011 54/199 Les pointeurs La déclaration des pointeurs se fait de la manière suivante: type *identificateur; Exemple: int *var; On dit que var est une variable(pointeur) qui pointe sur les valeurs de type int ou encore var est une variable qui peut contenir l'adresse d'une variable de type int. Année Académique: 2010-2011 55/199 Les pointeurs Exemple: int x=1; /* x est de type int */ int y=2; /* y est de type int */ int *ip; /* ip est de type int * */ ip=&x; /* ip pointe sur x */ y=*ip ; /* y contient la valeur pointée par ip */ *ip=0 ; /* la valeur pointée par ip vaut 0 */ x=?? y= ?? Année Académique: 2010-2011 56/199 Les pointeurs Les pointeurs peuvent être utilisés directement sans être déréferencés. Ainsi on a le droit de faire ip=iq si ip et iq sont deux pointeurs de même type. Il existe une valeur qui peut être prise par n'importe quel type de pointeur. Il s'agit du pointeur nul (valeur 0) pour indiquer qu'un pointeur pointe nulle part. Année Académique: 2010-2011 57/199 Conversion des pointeurs Il n'existe aucune conversion implicite d'un type de pointeur dans un autre. Il est toujours possible de faire appel à l'opérateur de cast. Année Académique: 2010-2011 58/199 Le pointeur générique Le type void * joue un rôle particulier. Il définit un type de pointeur générique. Ce type de pointeur est compatible avec tous les autres types de pointeurs. Un pointeur de ce type peut pointer vers n'importe quel type. Il s'agit d'un type à part entière (et non pas d'un pointeur vers quelque chose de type void, ce qui n'existe pas) Année Académique: 2010-2011 59/199 Le pointeur générique Ce type de pointeur joue un rôle central dans le prototypage des fonctions pour désigner: ● Le type retourné de fonctions renvoyant des pointeurs sur des zones de données structurées librement par les applications. ● Le type de pointeurs transmis en paramètres (si tout type de pointeur est acceptable) Année Académique: 2010-2011 60/199 Le pointeur générique Une variable de type void * ne peut pas intervenir dans des opérations arithmétiques; Notamment si p et q sont de type void *, on ne peut pas parler de p+i ou p-q Année Académique: 2010-2011 61/199 Priorité des opérateurs Les opérateurs & et * ont la même priorité que les autres opérateurs unaires. Ils sont aussi associatifs à gauche. Année Académique: 2010-2011 62/199 Priorité des opérateurs. Si ip pointe sur la valeur x, alors *ip peut être utilisé n'importe où x peut être utilisé. Exemple: Si on a: int x=10 ;int *ip=&x; *p=*p+10; est équivalent à x=x+10; int y=*ip +1; équivalent à int y=x+1; *ip+=1; est équivalent à x+=1; ++*ip; est équivalent à ++x; (*ip)++ est équivalent à x++; Année Académique: 2010-2011 63/199 Pointeurs et les fonctions En C, par défaut les paramètres sont passés par valeur aux fonctions. Que si la fonction doit changer l'un de ses arguments. Exemple: Échanger le contenu de deux variables. void swap(int a, int b){ int c=a; a=b; b=c; } Ce code ne marche pas Année Académique: 2010-2011 64/199 Pointeurs et les fonctions La solution consiste donc à passer les paramètres par adresses. Exemple: int n; scanf("%d",&n); Ainsi, si on a: int x=10, y=20; On doit écrire swap(&x,&y); /* ERREUR car &x n'est pas de type int */ Année Académique: 2010-2011 65/199 Pointeurs et les fonctions On doit donc changer la signature de swap. Les valeurs des paramètres doivent être passées indirectement. void swap(int *x, int *y){ int c; c=*x; *x=*y; *y=c ; } Année Académique: 2010-2011 66/199 Pointeurs et les fonctions Les arguments passés par adresse permettent aux fonctions de modifier leur valeur. Ce mode de passage est très important quand la fonction abstrait un traitement (au lieu de calculer une valeur). Année Académique: 2010-2011 67/199 Pointeurs et tableaux En C, il y a une forte relation entre les pointeurs et les tableaux. Toutes les opérations faites à travers les tableaux peuvent être faites à travers les tableaux. Les versions faites au travers des pointeurs est en général plus rapide. En fait le nom d'un tableau contient l'adresse du premier élément du tableau. Année Académique: 2010-2011 68/199 Pointeurs et tableaux Exemple: int vecteur[10]; déclare 10 emplacements qui peuvent contenir des valeurs de type int. La notation vecteur[i] fait référence au ième élément du tableau. int *pa=&vecteur[0]; pa contient l'adresse du premier élément du tableau pa+1 l'adresse du deuxième élément. Année Académique: 2010-2011 69/199 Pointeurs et tableaux Il y a une différence entre le nom d'un vecteur et le pointeur. Le pointeur est une variable, ainsi on peut écrire pa=a ou pa++ si pa est un pointeur; Si a est le nom d'un tableau, alors les instructions a=e et a++ sont illégales. Année Académique: 2010-2011 70/199 Pointeurs et tableaux Lorsque le nom d'un tableau est passé à une fonction, ce que l'on transmet est en réalité l'adresse du premier élément du tableau. À l'intérieur de la fonction, cette variable est une copie locale. Année Académique: 2010-2011 71/199 Exemple Écrire une fonction qui calcule le nombre de caractères d'une chaîne de caractères. int strlen(char *s){ int n ; for(n=0; *s!='\0';s++) n++; return n; } Année Académique: 2010-2011 72/199 Opérations arithmétiques Si p est un pointeur alors p++ incrémente p pour pointer sur le prochain élément et p+=i ajoute i à l'adresse de p pour pointer sur ieme élément qui se trouve après p. En général, un pointeur peut être initialisé comme n'importe quelle variable mais en général les seules valeurs significatives sont 0 et une expression qui fait référence à des adresses d'autres variables. Année Académique: 2010-2011 73/199 Opérations arithmétiques Les pointeurs et les entiers ne sont pas interchangeables. 0 est la seule exception. La constance symbolique NULL définie dans stdio.h est souvent utilisé à la place de 0. Année Académique: 2010-2011 74/199 Opérations arithmétiques Si p et q sont des pointeurs qui appartiennent au même tableau, alors les opérations ==,<,>,<=,=> et != peuvent être utilisé. Ainsi p<q est vrai si p pointe sur un élément qui se trouve avant l'élément pointé par q dans le tableau. Année Académique: 2010-2011 75/199 Opérations Les seules opérations valides sur les pointeurs sont donc l'affectation des pointeurs de même type, ajouter ou soustraire un entier à un pointeur, soustraire ou comparer deux pointeurs qui appartiennent au même tableau, affectation ou comparaison avec 0. Année Académique: 2010-2011 76/199 Pointeurs et chaînes de caractères En C, il n'existe pas de type pour représenter les chaînes de caractères. Elles sont représentées comme des pointeurs sur des caractères. Année Académique: 2010-2011 77/199 Les chaînes constantes Une chaîne constante est mise entre les doubles quotes. "je suis une chaine" ; est représenté intérieurement comme un tableau de caractère terminé avec le caractère nul '\0'. Le nombre d'emplacement requis est donc égal au nombre de caractère +1. Année Académique: 2010-2011 78/199 Les pointeurs et chaînes On peut affecter la valeur d'une chaîne constante à un pointeur de type char *. Ainsi char *pmessage="Bonjour" Affecte à pmessage un pointeur sur le tableau des caractères. Il ne s'agit pas d'une opération de copie de caractères mais juste de manipulation des pointeurs. Année Académique: 2010-2011 79/199 Les pointeurs et chaînes Il existe une différence fondamentale entre le nom d'un tableau de caractère et un pointeur sur les caractères. Exemple: char m[20]="Hello boy"; char * pm="Good morning"; m désigne ici l'emplacement utilisé pour stocker la chaînes. Les caractères peuvent changer individuellement mais on ne peut pas changer m. Année Académique: 2010-2011 80/199 Les pointeurs et chaînes Par contre pm pointe sur un emplacement mémoire qui contient la chaîne constante. La valeur du pointeur peut changer pour pointer quelque part d'autre mais la modification de la chaîne constante est indéfinie Année Académique: 2010-2011 81/199 Opérations sur les chaînes La bibliothèque string.h définie un ensemble de fonctions sur les chaînes de caractères: ● strcpy copie une chaine dans une autre ● strcat copie une chaine à la suite d'une autre ● strcmp compare deux chaînes ● strchr fournit un pointeur sur la première occurrence d'un caractère ● strlen renvoie la longueur d'une chaine ● memcpy opération de recopie mémoire Année Académique: 2010-2011 82/199 Pointeurs de fonctions En C, contrairement à d'autres langages, les fonctions ne sont pas des variables. Il est cependant possible de définir des pointeurs sur les fonctions qui peuvent être affectés, placés dans des tableaux, passés en argument à des fonctions ou retournés par les fonctions. Année Académique: 2010-2011 83/199 Pointeurs de fonctions La définition d'une fonction crée par défaut un pointeur sur une fonction. D'une manière générales, la définition d'une fonction fonc de prototype T fonc(T1,T2,..,Tn) définit un pointeur constant (non modifiable) de type incomplet (utilisable uniquement comme paramètre dans un prototype sans nommage des paramètres) T (*)(T1,T2,..,Tn) Année Académique: 2010-2011 84/199 Pointeurs de fonctions La définition d'un pointeur ptr_fonc vers une fonction telle que fonc est réalisée par : T (*ptr_fonc)(T1,T2,..,Tn). On peut en déduire la définition d'un nom de type pour ces pointeurs : typedef T(* pointeur_fonction) (T1,T2,..Tn) qui autorise une définition telle que: pointeur_fonction ptr_fonc; Année Académique: 2010-2011 85/199 Exemple Définition d'un pointeur sur une fonction ayant 2 paramètre de type int et renvoyant un int: typedef int (* pointeur)(int, int); ● Définition d'une fonction renvoyant un entier et ayant en paramètres un pointeur sur une fonction renvoyant un int et ayant deux int en paramètres et deux int supplémentaires en paramètres: int calc(int (*)(int, int),int,int) ● Année Académique: 2010-2011 86/199 Questions Expliquer la déclaration suivante: ● ● void *(*start_routine)(void *) void (*signal (int sig, void (*fcn)(int)))(int) Année Académique: 2010-2011 87/199 Exercice Écrire une fonction qui ne renvoie aucune valeur et qui déterminer la valeur maximale et la valeur minimale d'un tableau d'entiers (à un indice) de taille quelconque. Il faudra donc prévoir 4 arguments : le tableau, sa dimension, le maximum et le minimum. ● Modifier l'algorithme du tri par bulles de telle sorte qu'il prenne en paramètre une fonction de comparaison de deux entiers. ● Année Académique: 2010-2011 88/199 Exercice. On considère le problème d'échange de deux variables. void swap(int *x, int *y){ int c; c=*x; *x=*y; *y=c ; } Que faire si l'on veut échanger deux floats? Année Académique: 2010-2011 89/199 Exercice Modifier la fonction swap de telle sorte qu'elle puisse échanger n'importe quel type de valeurs (les deux valeurs étant de même type). Hint: Utiliser la fonction memcpy définie dans string.h Année Académique: 2010-2011 90/199 LA GESTION DYNAMIQUE DE LA MÉMOIRE Année Académique: 2010-2011 91/199