Vous êtes sur la page 1sur 28

AGP: Algorithmique et programmation

Tanguy Risset, Stéphane Ubéba


tanguy.risset@insa-lyon.fr, Stéphane.Ubéda@insa-lyon.fr
Lab CITI, INSA de Lyon
Version du November 20, 2007

- p. 1/56

Les arbres

Les arbres
● Arbres
■ Un arbre est défini de manière récursive, c’est:
● Arbres binaires ◆ Soit un arbre atomique (ou noeud terminal ou feuille)
● ABR
● Tas ◆ Soit un noeud ayant pour fils d’autre arbres.
● AST
● Hash code ■ En général on étiquete les noeuds avec une valeur.
Introduction
N1
Pointeur de fonctions

Erreurs courante en C
■ Le noeud N1 est la racine de l’arbre.
N6
■ Les noeuds N3, N4, N5 et N8 sont N2

des feuilles de l’arbre.


■ Les noeuds N2, N6, N7 sont des N7
N3 N4 N5
noeuds internes de l’arbre.
■ Le noeud N1 est le père des noeuds
N2 et N6. N8

■ La racine est un ancêtre de tous les


noeuds de l’arbre.

- p. 2/56
Parcours d’un arbre binaire

Les arbres
● Arbres
■ Un arbre binaire est un arbre dont les noeuds ont au plus
● Arbres binaires
● ABR
deux fils
● Tas
● AST
■ Les algorithmes travaillant sur des arbres sont généralement
● Hash code récursifs.
Introduction
■ En langage de description algorithmique, on suppose
Pointeur de fonctions
disposer de fonctions d’accès: à partir de chaque noeud N
Erreurs courante en C
on peut atteindre son père pere(N) et ses fils filsDroit(N),
filsGauche(N)
PRODECURE parcours(ARBRE N)
SI filsGauche(N)6= NULL ALORS parcours(filsGauche(N)) FINSI
traiter(N);
SI filsDroit(N)6= NULL ALORS parcours(filsDroit(N)) FINSI
FIN

- p. 3/56

Arbre d’une expression arithmétique

Les arbres
● Arbres *
● Arbres binaires
● ABR
● Tas
● AST + +
● Hash code

Introduction

Pointeur de fonctions
5 * * *
Erreurs courante en C

2 3 10 10 9 9

■ Si la procédure traiter(N) affiche la valeur d’un noeud, on


obtient:
■ 5 + 2 ∗ 3 ∗ 10 ∗ 10 + 9 ∗ 9
■ Est ce l’expression représentée par l’arbre?

- p. 4/56
Affichage d’une expression arithmétique

Les arbres
*
● Arbres
● Arbres binaires
● ABR
+ +
● Tas
● AST
● Hash code
5 * * *
Introduction

Pointeur de fonctions 2 3 10 10 9 9

Erreurs courante en C
PRODECURE parcours(ARBRE N)
SI (valeur(N)==’+’) ALORS print(’(’) FINSI
SI filsGauche(N)6= NULL ALORS parcours(filsGauche(N)) FINSI
PRINT(valeur(N));
SI filsDroit(N)6= NULL ALORS parcours(filsDroit(N)) FINSI
SI (valeur(N)==’+’) ALORS print(’)’) FINSI
FIN
■ En rajoutant des parenthèses autour d’une addition:
■ (5 + 2 ∗ 3) ∗ (10 ∗ 10 + 9 ∗ 9)

- p. 5/56

Différents parcours d’un arbre

Les arbres
● Arbres
■ Suivant l’ordre dans lequel on écrit les instructions de la
● Arbres binaires
● ABR
fonction de parcours, l’ordre de parcours des noeuds est
● Tas différent.
● AST
● Hash code ■ Trois possibilités principales:
Introduction ◆ Parcours préfixe : racine, sous-arbre gauche, sous-arbre
Pointeur de fonctions droit
Erreurs courante en C ◆ Parcours infixe : sous-arbre gauche, racine, sous-arbre
droit
◆ Parcours postfixe : sous-arbre gauche, sous-arbre droit,
racine
■ Il existe d’autres types de parcours moins facile a réaliser:
◆ parcours en largeur d’abord.

- p. 6/56
Parcours préfixe

Les arbres
1
● Arbres *
● Arbres binaires
● ABR
● Tas
2 + 7 +
● AST
● Hash code

Introduction
3 4
5 * 8 * 11 *
Pointeur de fonctions

Erreurs courante en C

5 2 6 3 9 10 10 10 12 9 13 9

PRODECURE parcoursPrefixe(ARBRE N)
traiter(N);
SI filsGauche(N)6= NULL ALORS parcours(filsGauche(N)) FINSI
SI filsDroit(N)6= NULL ALORS parcours(filsDroit(N)) FINSI
FIN

* ( + 5 * 2 3 ) (+ * 10 10 * 9 9)

- p. 7/56

Parcours infixe

Les arbres 6
● Arbres *
● Arbres binaires
● ABR
● Tas
2 + 10 +
● AST
● Hash code

Introduction
1 4 8 12
Pointeur de fonctions
5 * * *
Erreurs courante en C

3 5 7 9 11 13
2 3 10 10 9 9

PRODECURE parcoursInfixe(ARBRE N)
SI filsGauche(N)6= NULL ALORS parcours(filsGauche(N)) FINSI
traiter(N);
SI filsDroit(N)6= NULL ALORS parcours(filsDroit(N)) FINSI
FIN

( 5 + 2 * 3 ) * ( 10 * 10 + 9 * 9 )

- p. 8/56
Parcours postfixe

Les arbres 13
● Arbres *
● Arbres binaires
● ABR
● Tas
● AST 5 + 12 +
● Hash code

Introduction
1 4 8 11
Pointeur de fonctions
5 * * *
Erreurs courante en C

2 3 3 6 9 10
2 10 7 10 9 9

PRODECURE parcoursPostfixe(ARBRE N)
SI filsGauche(N)6= NULL ALORS parcours(filsGauche(N)) FINSI
SI filsDroit(N)6= NULL ALORS parcours(filsDroit(N)) FINSI
traiter(N);
FIN

( 5 2 3 * + ) (10 10 * 9 9 * +) *

- p. 9/56

Implémentation en C

Les arbres
● Arbres
■ Pour un arbre binaire:
● Arbres binaires struct model_noeud
● ABR
● Tas {
● AST
● Hash code int val;
Introduction struct model_noeud *filsGauche;
Pointeur de fonctions struct model_noeud *filsDroit;
Erreurs courante en C } ;

typedef struct model_noeud NOEUD;

typedef NOEUD *ARBRE;

- p. 10/56
Implémentation en C

Les arbres
● Arbres
■ Pour un arbre N-aire (N≤10):
● Arbres binaires
● ABR
#define N 10
● Tas
● AST
● Hash code struct model_noeud
Introduction {
Pointeur de fonctions int val;
Erreurs courante en C struct model_noeud *enfants[N];
} ;

typedef struct model_noeud NOEUD;

typedef NOEUD *ARBRE;

- p. 11/56

Construction d’un arbre en C

Les arbres
● Arbres
ARBRE nouvelArbre(int val, ARBRE fg, ARBRE fd)
● Arbres binaires {
● ABR
ARBRE temp;
● Tas
● AST
● Hash code
temp= (ARBRE)malloc(sizeof(NOEUD));
Introduction temp->val=val;
Pointeur de fonctions
temp->filsGauche=fg;
temp->filsDroit=fd;
Erreurs courante en C
return(temp);
}
int main()
{

ARBRE arbre,temp1,temp2;

temp1=nouvelArbre(10,NULL,NULL);
temp2=nouvelArbre(20,NULL,NULL);
arbre=nouvelArbre(30,temp1,temp2);
fprintf(stdout,"filsdroit->val=%d\n",arbre->filsDroit->val);
}

- p. 12/56
Cas particulier des arbres binaires

Les arbres
● Arbres *
● Arbres binaires
● ABR
● Tas
● AST + +
● Hash code

Introduction

Pointeur de fonctions
5 * * *
Erreurs courante en C

2 3 10 10 9 9

■ Un arbre binaire à N étages (i.e. profondeur N − 1) contient


au plus 2N − 1 noeuds.
■ De plus il a au plus 2i noeuds de profondeur i.
■ On peut donc utiliser un tableau de taille 2N − 1 = ΣN −1 i
i=0 2
de la manière suivante
■ Le niveau i de l’arbre utilisera les cases d’adresses entre
2i − 1 et 2i+1 − 2. En effet: 2i+1 − 2 − (2i − 1) + 1 = 2i

- p. 13/56

Arbres binaires et tableaux

Les arbres
1
● Arbres *
● Arbres binaires
● ABR
● Tas
2 + 3 +
● AST
● Hash code

Introduction
4 5 6 7
Pointeur de fonctions
5 * * *
Erreurs courante en C

8 10 11 10 12 9 13
2 9 3 10 9

■ On range donc les noeuds de l’arbre par étage (parcours en


largeur d’abord). Si des noeuds sont absent sur un étage, on
laisse leur case vide. Le tableau a alors cette propriété: le
fils gauche du noeud rangé dans la
case i est à l’adresse 2i + 1, son fils droit est à l’adresse 2i + 2
index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 1
valeur * + + 5 * * * X X 2 3 10 10 9 9
■ indexFilsGauche(i)=2i+1, indexFilsDroit(i)=2i+2,
indexPere(i)=i div 2;
- p. 14/56
Exemple

Les arbres
1
● Arbres *
● Arbres binaires
● ABR
● Tas
2 + 3 +
● AST
● Hash code

Introduction
4 5 6 7
Pointeur de fonctions
5 * * *
Erreurs courante en C

8 10 11 10 12 9 13
2 9 3 10 9

index 0 1 2 3 4 5 6 7 8 9 10 11 12 13
valeur * + + 5 * * * X X 2 3 10 10 9
i=1
2i + 1 = 3 2i + 2 = 4

- p. 15/56

Exemple

Les arbres
1
● Arbres *
● Arbres binaires
● ABR
● Tas
2 + 3 +
● AST
● Hash code

Introduction
4 5 6 7
Pointeur de fonctions
5 * * *
Erreurs courante en C

8 10 11 10 12 9 13
2 9 3 10 9

index 0 1 2 3 4 5 6 7 8 9 10 11 12 1
valeur * + + 5 * * * X X 2 3 10 10
i=5
2i + 1 = 10 2i + 2 = 10

- p. 16/56
Arbres binaires de recherche

Les arbres
● Arbres
■ Ce sont des arbres binaires de valeurs ordonnées avec les
● Arbres binaires
● ABR
propriétés:
● Tas ◆ Tous les noeuds du sous arbre gauche d’un noeud ont
● AST
● Hash code une valeur inférieure (ou égale) à la sienne
Introduction ◆ Tous les noeuds du sous arbre droit d’un noeud ont une
Pointeur de fonctions valeur supérieure (ou égale) à la sienne
Erreurs courante en C ■ Utilisé pour stocker et rechercher rapidement des éléments
dans une table qui évolue rapidement.
20

3 25

3 12 21 26

8 13

8 6
- p. 17/56

Insertion en ABR
PRODECURE insertionABR(ELEMENT v, ARBRE A)
SI (A==NULL) ALORS
Les arbres
● Arbres
A=nouvelArbre(v,NULL,NULL)
● Arbres binaires SINON
● ABR
● Tas SI (v<valeur(A)) ALORS
● AST
● Hash code insertionABR(filsGauche(A))
Introduction SINON
Pointeur de fonctions insertionABR(filsDroit(A))
Erreurs courante en C FINSI
FINSI
FIN
20 20

3 25 3 25

3 12 21 26 3 12 21 26

8 13 8 13

8 8 6 9
6

- p. 18/56
Arbre équilibré

Les arbres
● Arbres
■ Un arbre binaire est équilibré (AVL pour Adel’son, Vel’Skii et
● Arbres binaires
● ABR
Landis) si pour tout noeud, la différence de profondeur entre
● Tas l’arbre de son fils gauche et l’arbre de son fils droit est au
● AST
● Hash code plus 1 (i.e. 0 ou 1).
Introduction ■ On peut montrer que la hauteur d’un AVL de N noeuds est
Pointeur de fonctions de l’ordre de Log(N )
Erreurs courante en C
■ On peut aussi modifier la procédure d’insertion dans un
arbre de recherche pour que l’arbre résultant soit toujours
équilibré.
■ Pour cela on utilise la transformation de rotation
B A

A B
c a

a b b c

- p. 19/56

File de priorité

Les arbres
● Arbres
■ Une file de priorité est une liste dans laquelle on peut ajouter
● Arbres binaires
● ABR
ou supprimer des éléments, chaque élément ayant une
● Tas priorité. Lors d’une suppression on prend toujours l’élément
● AST
● Hash code de priorité maximum.
Introduction ■ Comment implémenter une file de priorité pour que les
Pointeur de fonctions opérations d’insertion et de suppression aient une
Erreurs courante en C complexité de O(1)
■ On va utiliser une structure de tas.

- p. 20/56
Tas

Les arbres
● Arbres
■ Un tas est un arbre binaire avec les propriétés suivante:
● Arbres binaires ◆ La valeur de chaque noeud est supérieure ou égales à
● ABR
● Tas celle de ces fils.
● AST
● Hash code ◆ L’arbre est quasi complet: tous les étages sont complet
Introduction sauf éventuellement l’étage des feuilles et toutes les
Pointeur de fonctions feuilles de l’étage des feuilles sont regroupées à gauche.
Erreurs courante en C 16

14 10

8 10 9 3

2 4 7

index 0 1 2 3 4 5 6 7 8 9 10 11 12 13
valeur 16 14 10 8 10 9 3 2 4 7 X X X X

- p. 21/56

Algorithme d’insertion dans un tas

Les arbres
● Arbres
■ On sait quelle noeud doit être ajouté: c’est le premier noeud
● Arbres binaires
● ABR
libre de l’étage des feuilles, c.a.d La première case libre du
● Tas tableau.
● AST
● Hash code ■ Mais la valeur de ce nouveau noeud est peut être supérieure
Introduction
à son père. Dans ce cas on fait remonter le noeud en
Pointeur de fonctions l’échangeant avec son père.
Erreurs courante en C 16 16

14 10 14 10

8 10 9 3 8 15 9 3

2 4 7 15 2 4 7 10

16

15 10

8 14 9 3

2 4 7 10 - p. 22/56
Suppression dans un tas

Les arbres
● Arbres
■ L’élément à supprimer est toujours la racine (il reste deux
● Arbres binaires
● ABR
arbres)
● Tas
● AST
■ On place le dernier élément de la ligne des feuille en racine
● Hash code
et on le redescend jusqu’à sa place:
Introduction

16 10
Pointeur de fonctions

Erreurs courante en C
15 10 15 10

8 14 9 3 8 14 9 3

2 4 7 10 2 4 7

15 15

10 10 14 10

8 14 9 3 8 10 9 3

2 4 7 2 4 7

- p. 23/56

Complexité

Les arbres
● Arbres
■ Les opérations d’insertion et de retrait et de recherche dans
● Arbres binaires
● ABR
un tas de taille N se font en temps Log(N )
● Tas
● AST
■ On peut utiliser ces propriétés pour construire un algorithme
● Hash code
de tri qui est toujours en N Log(N )
Introduction
■ On insère les éléments dans un tas, puis on les retire un à
Pointeur de fonctions
un.
Erreurs courante en C
■ En pratique, cette technique est moins bonne que QuickSort:
les constantes sont trop grandes.
PRODECURE insertionTas(ELEMENT v, TAS A)
pos=premiereCaseLibre(A)
TANTQUE (A[pos div 2]≤ v) et pos > 0)
A[pos]=A[pos div 2]
pos=pos div 2
FINTANTQUE
A[pos]=v
FIN

- p. 24/56
Arbre de syntaxe abstrait

Les arbres
● Arbres
■ Lorsque l’on veut stocker une expression arithmétique, on a
● Arbres binaires
● ABR
deux types de noeuds: entiers et opérateurs.
● Tas (5 + 2 ∗ 3) ∗ (10 ∗ 10 + 9 ∗ 9)
● AST
● Hash code ■ Quelle structure de donnée utiliser pour stocker une telle
Introduction
expression?
Pointeur de fonctions
■ Une solution souvent utilisée est une hiérarchie de structure
Erreurs courante en C
combinée avec des unions

- p. 25/56

Retour sur les constructeurs de type

Les arbres
● Arbres
■ Énumération: Permet de définir des constantes:
● Arbres binaires
● ABR
enum {LUNDI, MARDI, MERCREDI}
● Tas
● AST
Définit trois constantes entières avec les valeur LUNDI=0,
● Hash code MARDI=1 et MERCREDI=2. On peut forcer leurs valeur:
Introduction enum {LUNDI=12, MARDI=13, MERCREDI=14 }
Pointeur de fonctions
■ Utilisé pour manipuler des informations symboliques.
Erreurs courante en C

- p. 26/56
Retour sur les constructeurs de type

Les arbres
● Arbres
■ Union: permet de manipuler des variables pouvant prendre
● Arbres binaires
● ABR
deux types différents: par exemple, soit un entier, soit une
● Tas chaîne de caractère. La définition est similaire à celle d’une
● AST
● Hash code structure (info est l’étiquette de l’union):
Introduction union info
Pointeur de fonctions {
Erreurs courante en C int val;
char *oper;
};
union info n;
■ L’utilisation se fait comme pour une structure (struct) sauf
qu’une variable n ne possède que l’un des deux champs:
val ou oper:
n.val=1 ou n.oper=(char *)malloc(2*sizeof(char));
strcpy(oper,"-");

- p. 27/56

Type pour stocker une expression

Les arbres
● Arbres typedef enum
● Arbres binaires
● ABR
{VALEUR, MULT, ADD, DIV, SUB} typeNoeud;
● Tas
● AST
● Hash code struct model_noeud
Introduction {
Pointeur de fonctions typeNoeud type;
Erreurs courante en C struct model_noeud *filsGauche ;
struct model_noeud *filsDroit ;
union
{
int val;
char *oper;
} info;
};

typedef struct model_noeud NOEUD;

typedef NOEUD *ARBRE;


- p. 28/56
Allocation des feuilles

Les arbres
● Arbres
Le champ type aide a determiner si la valeur est entière ou
● Arbres binaires
● ABR
chaîne de caractère.
● Tas ARBRE nouvelFeuille(int val)
● AST
● Hash code {
Introduction ARBRE temp;
Pointeur de fonctions

Erreurs courante en C temp= (ARBRE)malloc(sizeof(NOEUD));


temp->info.val=val;
temp->type=VALEUR;
temp->filsGauche=NULL;
temp->filsDroit=NULL;
return(temp);
}

- p. 29/56

Allocation des noeud internes


ARBRE nouvelOper(typeNoeud type, ARBRE fg, ARBRE fd)
{
ARBRE temp;
Les arbres
char *oper;
● Arbres
● Arbres binaires
● ABR
temp= (ARBRE)malloc(sizeof(NOEUD));
● Tas
● AST oper=(char *)malloc(2*sizeof(char));
● Hash code
switch (type)
Introduction {
Pointeur de fonctions
case MULT:
strcpy(oper,"*");
Erreurs courante en C
temp->type=MULT;
break;
case ADD:
strcpy(oper,"+");
temp->type=ADD;
break;
[...]
default: fprintf(stderr,"operateur non implementé\n");exit(-1);
}
temp->info.oper=oper;
temp->filsGauche=fg;
temp->filsDroit=fd;
return(temp);
}
- p. 30/56
Parcours de la structure

Les arbres
● Arbres
int afficherArbre(ARBRE arbre)
● Arbres binaires {
● ABR
if ((arbre->type==ADD)||(arbre->type==SUB))
● Tas
● AST fprintf(stdout,"(");
● Hash code
switch (arbre->type)
Introduction {
Pointeur de fonctions
case VALEUR: fprintf(stdout,"%d",arbre->info.val);
break;
Erreurs courante en C
default:
afficherArbre(arbre->filsGauche);
fprintf(stdout,"%s",arbre->info.oper);
afficherArbre(arbre->filsDroit);
};
if ((arbre->type==ADD)||(arbre->type==SUB))
fprintf(stdout,")");
}

- p. 31/56

Exemple d’utilisation

Les arbres
● Arbres
int main()
● Arbres binaires {
● ABR
● Tas
● AST ARBRE arbre, temp1,temp2,temp3;
● Hash code

Introduction

Pointeur de fonctions
temp1=nouvelFeuille(10);
temp2=nouvelFeuille(20);
Erreurs courante en C
temp3=nouvelFeuille(400);
arbre=nouvelOper(ADD,temp1,temp2);
arbre=nouvelOper(MULT,arbre,temp3);

afficherArbre(arbre);
fprintf(stdout,"\n");

return(0);
}
■ resultat:
(10+20)*400

- p. 32/56
Tables de hachage

Les arbres
● Arbres
■Beaucoup d’applications utilisent des dictionnaires:
● Arbres binaires
● ABR
collection dynamique d’objets accédés fréquemment.
● Tas
■ On utilise un attribut de l’objet comme clé pour retrouver
● AST
● Hash code l’objet dans la table stockant les objets.
Introduction
■ Exemple: carnet d’adresse indexé par le nom des
Pointeur de fonctions
personnes.
Erreurs courante en C
■ On ne peut pas trier la table car elle est dynamique.

■ On ne peut pas réserver une place pour toute les valeurs de


clés possible (trop grand).
⇒ On utilise une table à adressage dispersé: table de hachage
(Hash table).

- p. 33/56

Tables de hachage

Les arbres
● Arbres
■ La table de hachage utilise une fonction de hachage h pour
● Arbres binaires
● ABR
indexer la table contenant l’information: les informations
● Tas concernant "dupont" sont rangées à la case h(”dupont”)
● AST
● Hash code ■ La fonction de hachage est censée disperser les noms
Introduction
inégalement répartis (exemple: beaucoup de noms
Pointeur de fonctions commençant par "du").
Erreurs courante en C
■ Exemple de fonction pour des clés étant des chaînes de
P
caractères: h(x) = ( li=1 x[i]l−1 )modN
ou x[i] est le code ascii de la ieme lettre du nom.
■ Cette fonction donne au plus N index différents (compris
entre 0 et N ), elle est donc utilisé pour une table de taille N
■ Si deux noms ont le même index dans la table
(h(”dupont”) = h(”durand”)) on dit qu’il y a collision.
■ On résout les collisions en utilisant les cases vides de la
table.

- p. 34/56
Exemple de table de hachage

Les arbres
● Arbres
■ Hachage par collision séparées: une table pour indiquer
● Arbres binaires
● ABR
quelle est l’autre case prise en cas de collision.
● Tas paul i nom(i) tel(i) col(i)
● AST
● Hash code 0 pierre 2805 −1
Introduction
roger 1 −1
Pointeur de fonctions
2 −1
Erreurs courante en C

laure 3 paul 2811 −1

4 −1

5 roger 4501 12

anne 6 laure 2701 11

7 −1
pierre
8 −1

9 −1
yves 10 anne 2702 −1
collisions
11 yves 2806 −1

laurent 12 laurent 8064 10


- p. 35/56

13

Exemple de table de hachage

Les arbres
● Arbres
■ Hachage par adressage ouvert: on résout les collisions en
● Arbres binaires
● ABR
utilisant les cases suivantes du tableau.
paul i nom(i) tel(i)
● Tas
● AST
● Hash code 0 pierre 2805

Introduction 1
roger
Pointeur de fonctions
2
Erreurs courante en C

laure 3 paul 2811

5 roger 4501

anne 6 laure 2701

7 anne 2702
pierre
8 yves 2806

9 laurent 8064
yves 10

11

laurent 12
- p. 36/56
Intérêt des tables de hachage

Les arbres
● Arbres
■ L’intérêt est de s’approcher d’une structure à accès direct
● Arbres binaires
● ABR
(temps constant pour accéder à un élément à partir de sa
● Tas clé).
● AST
● Hash code ■ Si la fonction de hachage est parfaite (pas de collision), c’est
Introduction
gagné.
Pointeur de fonctions
■ En général elle n’est pas parfaite, de nombreuses
Erreurs courante en C
recherches ont sélectionnées des tables de hachage très
performantes pour divers types de données.
■ On a aussi montré que l’on pouvait augmenter ou diminuer
dynamiquement la taille de la table et donc avoir une
fonction de hachage qui est adapté au nombre de donnée
que l’on traite.

- p. 37/56

Exemple de Complexité d’accès

Les arbres
● Arbres
■ En ce qui concerne le hachage par adressage ouvert, on
● Arbres binaires
● ABR
peut montre que si α est le taux d’occupation de la table
● Tas (α = nbelt /tailletable ), le nombre d’opération en moyenne
● AST
● Hash code pour rechercher un élément est:
◆ 1 + 1
Introduction
2 2(1−α) pour une recherche avec succès
Pointeur de fonctions ◆ 1 + 1
pour une recherche avec échec
2 2(1−α)2
Erreurs courante en C
■ Pour α = 2/3 on fait 2 et 5 opérations. Pour α = 9/10 on fait
5 et 50 opérations.
■ Si on est prêt à a voir une table un peu plus grosse que
nécessaire on a des temps d’accès très bons.

- p. 38/56
Plan

Les arbres ■ Pointeurs de fonctions


Introduction
■ Les erreurs courantes en C (source
Pointeur de fonctions
http://nicolasj.developpez.com/articles/erreurs/
Erreurs courante en C
par exemple)

- p. 39/56

Utilité des pointeurs de fonction

Les arbres ■ Mécanismes dynamiques


Introduction ◆ plug-in
Pointeur de fonctions ◆ Modifier une fonctionnalité sans arrêter le programme
Erreurs courante en C ◆ ajouter de nouvelles fonctionnalités

■ Exemple: fonction de décodage de trame niveau 2:


dépendant de l’interface connectée (ethernet, wifi, etc.)

- p. 40/56
Un premier exemple

Les arbres
#include <stdio.h>
Introduction
#include <stdlib.h>
Pointeur de fonctions
//declaration de fonction
Erreurs courante en C
int fonct1(int a)
{
fprintf(stdout,"Je suis fonct1(%d)\n",a);
return(0);
}

int main()
{// declaration de pointeur de fonction
int (*foncPtr)(int a);

foncPtr=&fonct1;
(*foncPtr)(10);
return(0);
} - p. 41/56

Comprendre les déclarations

Les arbres ■ Déclaration d’une variable: int *q[3]


Introduction ◆ [] plus prioritaire que *, donc:
Pointeur de fonctions
int *q[3] ⇔ int (*(q[3]))
Erreurs courante en C ◆ l’expression (*(q[3])) est de type int
◆ l’expression q[3] est de type pointeur vers un int
◆ l’expression (i.e. la variable) q est de type tableau de
pointeur vers un int
■ Déclaration d’une fonction:
int fonct1(int a)
◆ l’expression fonct1(int a) est de type int
◆ l’expression (i.e. la variable) fonct1 est de type fonction
qui prend un int et renvoie un int
◆ Les parenthèses après un symbole indique que le
symbole est une fonction (de même que les crochets
après un symbole indique que le symbole est un tableau).

- p. 42/56
Déclaration d’un pointeur de fonction

Les arbres ■ Déclaration d’un pointeur de fonction:


Introduction
int (*foncPtr)(int a)
Pointeur de fonctions ◆ l’expression (*foncPtr)(int a) est de type int
Erreurs courante en C ◆ l’expression (*foncPtr) est de type fonction qui prend
un int est renvoie un int
◆ l’expression (i.e. la variable) foncPtr est de type pointeur
vers une fonction qui prend un int et renvoie un int
■ lors de l’utilisation, (presque) tout se passe comme si la
fonction était une Lvalue:
◆ On peut affecter une adresse de fonction au pointeur de
fonction: foncPtr=&fonct1;
◆ Si on déréférence le pointeur de fonction, on obtient une
fonction: l’exécution de (*foncPtr)(10); affiche:
Je suis la fonction fonct1(10)

- p. 43/56

En fait, c’est un peu plus compliqué...

Les arbres ■ En C, une fonction est automatiquement castée en pointeur


Introduction
de fonction (et inversement):
Pointeur de fonctions
foncPtr=&fonct1 ⇔ foncPtr=fonct1
Erreurs courante en C
(*foncPtr)(10); ⇔ (foncPtr)(10)
■ Tout comme pour les tableaux:
tab ⇔ &tab
■ Pour les fonctions et les tableaux qui ne sont pas des
L-values (on les appelle quelquefois des labels) le
compilateur identifie a et &a
■ On peut donc écrire:
int (*foncPtr)(int a);

foncPtr=fonct1;
foncPtr(10);

- p. 44/56
On peut donc ecrire:

Les arbres
#include <stdio.h>
Introduction
#include <stdlib.h>
Pointeur de fonctions
//declaration de fonction
Erreurs courante en C
int fonct1(int a)
{
fprintf(stdout,"Je suis fonct1(%d)\n",a);
return(0);
}

int main()
{// declaration de pointeur de fonction
int (*foncPtr)(int a);

foncPtr=fonct1;
foncPtr(10);
return(0);
} - p. 45/56

Un autre exemple

Les arbres
int main(void)
Introduction
{
Pointeur de fonctions
//comparaison de deux entiers int i,t[6]={1,5,2,3,6,4};
Erreurs courante en C

int croissant(int i, int j) trie(t, 6, croissant);


{ for(i=0;i<6;i++)
if (i<=j) return 0; fprintf(stdout," %d ",t[
else return 1; fprintf(stdout,"\n");
}
int decroissant(int i, int j) trie(t, 6, decroissant);
{ for(i=0;i<6;i++)
if (i<=j) return 1; fprintf(stdout," %d ",t[
else return 0; fprintf(stdout,"\n");
} return 0;
}

- p. 46/56
... la fonction tri

Les arbres
void trie(int tableau[], int taille, int (fcomp)(int, int))
Introduction
{
Pointeur de fonctions
int i,j,min;
Erreurs courante en C
//tri par permuation avec fcomp comme fonction de comparaiso
for (i=0;i<taille;i++)
{
min=tableau[i];
for (j=i+1;j<taille;j++)
if (fcomp(tableau[i],tableau[j]))
{
min = tableau[j];
tableau[j]=tableau[i];
tableau[i]=min;
}
}
return ;
} - p. 47/56

Passage de fonction par référence

Les arbres ■ Comme pour une variable normale, on peut modifier un


Introduction
pointeur de fonction en le passant en paramêtre par
Pointeur de fonctions
référénce.
Erreurs courante en C
■ Pour avoir une fonction qui modifie un pointeur de fonction
(i.e. qui modifie la qui fonction appelée lorsque le pointeur
est invoqué), il faut que son paramêtre soit un pointeur sur
un pointeur de fonction.

- p. 48/56
Passage de fonction par référence
changeOrdre(int (**fcomp1)(int, int), int (*fcomp2)(int, int))
{
Les arbres
*fcomp1=fcomp2;
Introduction
}
Pointeur de fonctions
int main(void)
Erreurs courante en C
{
int i,t[6]={1,5,2,3,6,4};
int (*fcomp)(int,int);

fcomp=croissant;
trie(t, 6, fcomp);
for(i=0;i<6;i++)
fprintf(stdout," %d ",t[i]);
fprintf(stdout,"\n");

changeOrdre(&fcomp,decroissant);

trie(t, 6, fcomp);
for(i=0;i<6;i++) - p. 49/56

fprintf(stdout," %d ",t[i]);

Les arbres

Introduction

Pointeur de fonctions

Erreurs courante en C

Les erreurs courantes en C

- p. 50/56
Confusion entre == et =

Les arbres
■ À ne pas faire:
Introduction
if (size = 0) ....
Pointeur de fonctions
■ Détection: l’option -Wall du compilateur avertit le
Erreurs courante en C
programmeur (warning)

- p. 51/56

Confusion entre opérateurs logiques et binaires

Les arbres ■ Le ET logique (&&) qui retourne 0 ou 1 (en s’arrêtant au


Introduction
premier argument s’il est faux)
Pointeur de fonctions
■ Le ET binaire (&) évalue ses deux opérandes et effectue le
Erreurs courante en C
ET bit à bit entre ses deux opérandes.
■ Le OU logique (||) qui retourne 0 ou 1 (en s’arrêtant au
premier argument s’il est vrai)
■ Le OU binaire (|) évalue ses deux opérandes et effectue le
OU bit à bit entre ses deux opérandes.
■ Impossible à détecter à la compilation.

- p. 52/56
Problèmes de macros

Les arbres ■ On n’écrit pas


Introduction
#define MAX 10;
Pointeur de fonctions ◆ A[MAX] devient A[10;]
Erreurs courante en C
■ On n’écrit pas
#define MAX=10
◆ Erreur détectée à la compilation, mais lors de l’utilisation
de MAX (la ligne référencée n’est pas celle de la définition
de MAX).
■ On écrit
#define MAX 10
■ En cas de doute, on peut utiliser gcc -E pour vérifier
l’expansion correcte des macros.

- p. 53/56

Fonctions retournant un caractère getc...

Les arbres char c;


Introduction while ( (c = getchar ()) != EOF)
Pointeur de fonctions ...
Erreurs courante en C ■ La fonction getchar retourne un entier

■ Les cast implicites effectués sont:


while ( (int)(c = (char)getchar ()) != EOF)
■ le caractère EOF (qui marque la fin d’un fichier : End Of File)
est un caractère invalide généralement égal à -1 mais il peut
parfaitement être égal à 128, dans ce cas on dépasse la
capacité de stockage d’un char et l’on se retrouve avec un
résultat égal à -128 : la condition du while sera toujours
fausse, le programme boucle indéfiniment!
■ Il faut écrire:
int cInt;
char c;
while ( (cInt = getchar ()) != EOF)
{
c=(char)cInt; - p. 54/56
Erreurs avec if et for

Les arbres ■ point-virgule mal placé


Introduction if (a < b) ; for (i=0; i<N; i++);
Pointeur de fonctions a = b; printf("%d", i);
Erreurs courante en C ■ Le mauvais else
if (a < b)
if (b < c) then b = c;
else
b = a;
■ Ce qu’il fallait faire:
if (a < b)
{ if (b < c) then b = c; }
else
b = a;

- p. 55/56

Et les plus courantes...

Les arbres ■ Mauvaise indentation


Introduction
■ Pas de makefile (ou pas de cible clean dans le makefile)
Pointeur de fonctions

Erreurs courante en C
■ exemple de fichier configuration vi:
syntax on " coloration syntaxique
set autoindent " identation
set cindent " pour C
set nopaste
set ts=4 " tabulation a 4 caracteres
set sw=4
■ Sous vi:
◆ == pour indenter la ligne courante,
◆ =G pour identer tout le fichier:

- p. 56/56

Vous aimerez peut-être aussi