Telechargé par kabrel angelo

Algo structure de donnee 1 Partie 1

publicité
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
Téléchargement