UE C avanc´e
Introduction
On s’int´eresse `a des structures de donn´ees donnant un acc`es aussi direct que possible au contenu. Une donn´ee est retrouv´ee `a partir d’une cl´e.
La cl´e permet de retrouver la donn´ee dans le tableau.
Tableau `a adressage direct : la cl´e est l’indice dans le tableau.
→ meilleure solution, acc`es en O(1)
Introduction
Contrainte: `a chaque cl´e correspond un espace m´emoire Probl`eme: comment faire pour stocker des donn´ees dont la cl´e serait un mot ?
I Mots de 25 lettres au maximum
I Nombre de cases n´ecessaires : 25625= 2200! ! (un Gigaoctet correspond `a 230 octets)
I En supposant 27 valeurs diff´erentes pour une lettre (26 +
’-’) : 2725>1625= 2100! !
Introduction
Objectif : trouver une structure de donn´ees et les fonctions associ´ees permettant de stocker des donn´ees avec une cl´e quelconque.
Contexte : l’intervalle des valeurs possibles pour les cl´es est beaucoup plus grand que le nombre de valeurs que l’on souhaite stocker.
Conclusion: on est prˆet `a renoncer `a la bijection
emplacement-cl´e. Afin de reduire la taille du tableau, on accepte que plusieurs cl´es diff´erentes pointent sur le mˆeme emplacement.
Fonction de correspondance : introduction
h(k)[0,taille−1]
h() est la fonction de correspondance.k est la cl´e utilis´ee,taille est la taille de la table de hachage.
Principe: associer une valeur repr´esentant un indice dans un tableau `a une valeur de type pr´ed´efini quelconque (chaˆıne de caract`eres, par exemple).
Objectif : restreindre l’espace des valeurs possibles. Une seule case de la table correspond `a plusieurs cl´es...
Gestion des collisions : hachage lin´eaire
Principe: en cas de collision, on incr´emente de 1 (modulotaille) l’indice trouv´e par la fonction de hachage. Si la case
correspondante est occup´ee, on continue jusqu’`a tomber sur une case vide.
Gestion des collisions : hachage lin´eaire
Structure de donn´ee pour stocker une valeur ?
t y p e d e f s t r u c t d o n n e e T a b l e D e H a s h ∗PDonneeTableDeHash ; t y p e d e f s t r u c t d o n n e e T a b l e D e H a s h {
c h a r ∗c l e ; c h a r ∗ d a t a ;
} DonneeTableDeHash ;
DonneeTableDeHash t a b l e a u [ t a i l l e ] ;
Gestion des collisions : hachage lin´eaire
Ajouter une entr´ee dans la table :
PDonneeTableDeHash c=t a b l e a u+h ( c l e , t a i l l e ) ; PDonneeTableDeHash d=c ;
w h i l e ( c−>c l e !=NULL) { i f ( ! s t r c m p ( c l e , c−>c l e ) ) {
p r i n t f ( ” C l e d e j a u t i l i s e e : %s\n” , c l e ) ; r e t u r n;
}
p r i n t f ( ” C o l l i s i o n l o r s de l ’ e c r i t u r e ” ) ; i f ( c−t a b l e a u == t a i l l e −1)
c=t a b l e a u ; e l s e
c++;
i f ( c == d ) {
p r i n t f ( ” T a b l e a u p l e i n !\n” ) ; r e t u r n;
} }
/∗ c p o i n t e s u r une c a s e v i d e . . . ∗/
Gestion des collisions : hachage lin´eaire
R´ecup´erer une entr´ee dans la table :
PDonneeTableDeHash c=t a b l e a u+h ( c l e , t a i l l e ) ; PDonneeTableDeHash d=c ;
i f ( c−>c l e==NULL) {
p r i n t f ( ” Donnee a b s e n t e , c l e : %s\n” , c l e ) ; r e t u r n NULL ;
}
w h i l e ( s t r c m p ( c−>c l e , c l e ) ) { i f ( c−t a b l e a u == t a i l l e )
c=t a b l e a u ; e l s e
c++;
i f ( c−>c l e==NULL) {
p r i n t f ( ” Donnee a b s e n t e , c l e : %s\n” , c l e ) ; r e t u r n NULL ;
}
i f ( c == d ) {
p r i n t f ( ” T a b l e a u p l e i n e t donnee a b s e n t e\n” ) ; r e t u r n NULL ;
} }
/∗ c p o i n t e s u r l a donnee r e c h e r c h e e ∗/
Gestion des collisions : hachage lin´eaire
Probl`eme : “Clustering”
en cas de conflits fr´equents, certaines zones vont se remplir plus vite que d’autres...
Plus il y a de conflits pour une mˆeme case, plus il sera long de trouver un espace disponible.
Gestion des collisions : Chaˆınage
Principe: chaque case du tableau est un pointeur sur une liste chaˆın´ee qui contient les donn´ees de toutes les cl´es indiquant cette case.
Gestion des collisions : Chaˆınage
Exemple : Table de hachage avec chainage.
Structure de donn´ee correspondante ?
Gestion des collisions : Chaˆınage
Structure de donn´ee correspondante ?
t y p e d e f s t r u c t d o n n e e T a b l e D e H a s h ∗PDonneeTableDeHash ; t y p e d e f s t r u c t d o n n e e T a b l e D e H a s h {
c h a r ∗c l e ; c h a r ∗ d a t a ;
PDonneeTableDeHash s u i v a n t ;
} DonneeTableDeHash ;
Gestion des collisions : Chaˆınage
Ajout d’un ´el´ement dans la table :
PDonneeTableDeHash c=t a b l e a u+h ( c l e , t a i l l e ) ;
i f ( c−>c l e !=NULL) {
i f ( ! s t r c m p ( c l e , c−>c l e ) ) {
p r i n t f ( ” C l e d e j a u t i l i s e e : %s\n” , c l e ) ; r e t u r n;
}
w h i l e ( c−>s u i v a n t !=NULL) {
i f ( ! s t r c m p ( c l e , c−>s u i v a n t−>c l e ) ) { p r i n t f ( ” C l e d e j a u t i l i s e e : %s\n” , c l e ) ; r e t u r n;
}
c=c−>s u i v a n t ; }
c−>s u i v a n t=m a l l o c (s i z e o f( DonneeTableDeHash ) ) ;
c=c−>s u i v a n t ;
c−>s u i v a n t=NULL ;
}
/∗ c p o i n t e s u r l ’ e l e m e n t a m o d i f i e r ∗/
Gestion des collisions : Chaˆınage
Acc`eder `a une donn´ee :
PDonneeTableDeHash c=t a b l e a u+h ( c l e , t a i l l e ) ;
i f ( c−>c l e==NULL) {
p r i n t f ( ” Donnee a b s e n t e , c l e : %s\n” , c l e ) ; r e t u r n NULL ;
}
w h i l e ( s t r c m p ( c−>c l e , c l e ) ) { c=c−>s u i v a n t ;
i f ( c==NULL| |c−>c l e==NULL) {
p r i n t f ( ” Donnee a b s e n t e , c l e : %s\n” , c l e ) ; r e t u r n NULL ;
} }
r e t u r n c ;
Bilan
Comparaison entre les avantages et inconv´enients des deux strat´egies :
Type Avantages Inconv´enients
Lin´eaire simple limite de taille, “clustering”
Chaˆınage pas de limite de taille occupation m´emoire, rapidit´e d’acc`es
Fonction de correspondance : objectifs
→calculer un indice `a partir d’une cl´e.
h(k)[0,taille−1]
h() =?
Cas d’une cl´e de type chaˆıne de caract`eres
Fonction de correspondance : principe
On transforme la chaˆıne de caract`eres en un entier.
Exemple : ajout des codes ASCII des caract`eres composant la chaˆıne.
“UE C avanc´e” : ’U’=85, ’E’=69, ’ ’=32, ’C’=67, ’ ’=32, ’a’=97,
’v’=118, ’a’=97, ’n’=110, ’c’=99, ’´e’=233 →total=1039 Remarque : en faisant cela tous les anagrammes d’un mot iront dans la mˆeme case.
Fonction de correspondance : solution intuitive
Si toutes les cl´es sont ´equiprobables et r´epartie dans un intervalle [0,r] :
h(k) =floor(((taille−1)∗k)/r)
Fonction de correspondance : modulo
On calcule le modulo : h(k) =k mod taille
taille doit ˆetre choisi convenablement. G´en´eralement, on choisit un nombre premier proche d’une puissance de 2, exemple : 4093.
Fonction de correspondance : produit
h(k) =floor(taille ∗(coeff ∗k−floor(coeff ∗k))) coeff est un flottant entre 0 et 1
UE C avanc´e cours 11: G´en´ericit´e
Jean-Lou Desbarbieux et St´ephane Doncieux UMPC 2012/2013
Sommaire
Introduction
Arguments de fonctions main
stdarg
Pointeurs sur fonction
Qu’est-ce que la g´en´ericit´e ?
Ecrire du code exploitable dans diff´erents contextes.´ Objectif :
I code robuste, permettant de g´erer diff´erents cas de figure
I r´eutilisation de code
Sensibilisation `a la g´en´ericit´e : programmes
Un programme doit pouvoir s’adapter aux besoins de l’utilisateur.
Pour un programme lanc´e `a partir d’une console, c’est faisable grˆace `a des arguments que l’utilisateur peut lui donner.
Exemples
Programmes utilitaires dans les distributions UNIX/LINUX : ls :
I -l : liste d´etaill´ee
I -t : tri´e par ordre de date
I -a : tout lister
I -d : lister les r´epertoires comme des fichiers simples
I ...
Sensibilisation `a la g´en´ericit´e : biblioth`eques
Int´erˆet : ´ecriture de fonctions r´eutilisables (biblioth`eques g´en´eriques), gain de temps lors de r´eutilisation...
Biblioth`eques r´eutilisables `a l’issue de cette UE :
I Listes chaˆın´ees
I Arbres
I Tables de hachage
Beaucoup de biblioth`eques d´ej`a disponibles sur le web. Exemple : GNU C library...
Exemples
Tir´es de la GNU C library :
I Gestion des chaˆınes de caract`eres
I Fonctions de hachage
I Fonctions de recherche dans des arbres
I Tri de tableaux
Sensibilisation `a la g´en´ericit´e
Lors de la cr´eation ou de l’utilisation d’une biblioth`eque
“g´en´erique” plusieurs crit`eres sont `a prendre en compte :
I performances : la g´en´ericit´e peut avoir un coˆut, utiliser une biblioth`eque g´en´erique peut ˆetre plus coˆuteux que de programmer quelques fonctions d´edi´ees
I rapport gains / pertes de temps :
I temps d’apprentissage par rapport au temps gagn´e `a utiliser la biblioth`eque
I temps dedebogage...
Sensibilisation `a la g´en´ericit´e
Dans la suite de ce cours, nous allons voir quelques m´ecanismes de base permettant d’impl´ementer des programmes ou des
biblioth`eques de fonctions “g´en´eriques”.
Arguments de fonctions
Arguments de fonctions : main
Arguments de la fonction main
Prototype :
int main(int argc, char *argv[]);
I argc : nombre d’arguments
I argv : arguments, tableau dechar *
Arguments de la fonction main
i n t main (i n t a r g c , c h a r ∗a r g v [ ] ) { u n s i g n e d i n t i ;
f o r ( i =0; i<a r g c ; i ++) {
p r i n t f ( ‘ ‘ Argument %d : %s\n ’ ’ , i , a r g v [ i ] ) ; }
r e t u r n 0 ; }
Arguments de la fonction main
bash$ ./main UE CAVE 2006 Argument 0 : ./main
Argument 1 : UE Argument 2 : CAVE Argument 3 : 2006 bash$
Arguments de la fonction main
Utilisation de getopt (fonction de la C GNU library) pour faciliter l’interpr´etation des param`etres.
#i n c l u d e <u n i s t d . h>
#i n c l u d e <s t d i o . h>
i n t main (i n t a r g c , c h a r ∗∗ a r g v ) { i n t c ;
w h i l e ( ( c=g e t o p t ( a r g c , a r g v , ” abcd : ” ))!=−1) { p r i n t f ( ” O p t i o n %c\n” , c ) ;
i f ( c== ’ d ’ ) {
p r i n t f ( ” argument : %s\n” , o p t a r g ) ; }
} }
Arguments de la fonction main
bash$ ./getopt -a -c -d coucou Option a
Option c Option d
argument: coucou
Arguments de fonctions : stdarg
Objectifs
Il n’est pas toujours possible de d´efinir a priori l’ensemble des arguments d’une fonction.
Exemple typique printf :
i n t p r i n t f (char ∗fmt , . . . ) ;
La gestion des fonctions `a nombre variable d’arguments
Il existe pour cela la biblioth`eque stdarg.h qui permet de g´erer les arguments d’une fonction d´eclar´ee comme suit :
t y p e f ( t y p e arg1 , . . . ) ;
Cette fonction peut ˆetre appel´ee avec un nombre variable d’arguments, eux-mˆemes de types variables, elle est dite
”variadique”.
Le fichier d’en-tˆete stdarg.h d´eclare un type va list et d´efinit trois macros permettant de parcourir la liste d’arguments dont le nombre et les types ne sont pas connus par la fonction appel´ee.
va list
v a l i s t
Ce type correspond `a un pointeur g´en´erique sur un argument.
va start
v o i d v a s t a r t ( v a l i s t ap , l a s t ) ;
La macro va start initialise ap pour les utilisations ult´erieures de va arg et va end, et doit donc ˆetre appel´ee en premier.
Le param`etre last est le nom du dernier param`etre avant la liste d’argument variable, c’est-`a-dire le dernier param`etre dont la fonction connaisse le type.
va arg
t y p e v a a r g ( v a l i s t ap , t y p e ) ;
La macro va arg se d´eveloppe en une expression qui a le type et la valeur de l’argument suivant de l’appel. Le param`etre ap est la va list ap initialis´ee par va start. Chaque appel de va arg modifie ap pour que l’appel suivant renvoie l’argument suivant. Le
param`etre type est le nom du type, indiqu´e de telle mani`ere qu’un pointeur sur un objet de ce type puisse ˆetre d´eclar´e simplement en ajoutant un ast´erisque `a type.
La premi`ere utilisation de la macro va arg apr`es celle de va start renvoie l’argument suivant last. Les invocations successives renvoient les valeurs des arguments restants.
va end
v o i d v a e n d ( v a l i s t ap ) ;
A chaque invocation de va start doit correspondre une invocation de va end dans la mˆeme fonction. Apr`es l’appel va end(ap) la variable ap est ind´efinie. Plusieurs travers´ees de la liste sont possible, `a condition que chacune soit encadr´ee par va start et va end. va end peut ˆetre une macro ou ue fonction.
exemple d’utilisation de stdarg.h
#i n c l u d e<s t d i o . h>
#i n c l u d e<s t d a r g . h>
v o i d m i n i p r i n t f (c h a r ∗fmt , . . . ) { v a l i s t pa ;
c h a r ∗p ,∗v a l s ; i n t v a l i ;
v a s t a r t ( pa , fmt ) ;
f o r( p=fmt ; ∗p ; p++) {
i f (∗p != ’% ’ ) {p u t c h a r (∗p ) ;c o n t i n u e; }
s w i t c h (∗++p ) {
c a s e ’ d ’ : v a l i = v a a r g ( pa , i n t) ; e c r i t e n t i e r ( v a l i ) ; b r e a k;
c a s e ’ s ’ : f o r( v a l s = v a a r g ( pa , c h a r ∗) ; ∗v a l s ; v a l s ++) p u t c h a r (∗v a l s ) ;
b r e a k; d e f a u l t : p u t c h a r (∗p ) ;
b r e a k; }}
v a e n d ( pa ) ;}
exemple d’utilisation de stdarg.h
v o i d e c r i t e n t i e r (i n t n ) { i n t m=n %10;
i f ( n>=10) {
e c r i t e n t i e r ( n / 1 0 ) ; }
p u t c h a r (m+ ’ 0 ’ ) ; }
i n t main ( ) {
m i n i p r i n t f ( ” d e b u t f o r m a t e n t i e r %d p u i s c h a i n e %s\n” , 3 , ” l a c h a i n e ” ) ;
r e t u r n 0 ; }
Pointeurs sur fonctions
Pointeurs sur fonctions : introduction
v o i d f 0 (v o i d) { . . . } v o i d f 1 (v o i d) { . . . } v o i d f 2 (v o i d) { . . . } i n t main (v o i d) {
u n s i g n e d i n t n ;
p r i n t f ( ” E n t r e r un numero s t r i c t e m e n t i n f e r i e u r a 3 : ” ) ; s c a n f ( ” %d” ,&n ) ;
s w i t c h( n ) { c a s e 0 :
f 0 ( ) ; b r e a k; c a s e 1 :
f 1 ( ) ; b r e a k; c a s e 2 :
f 2 ( ) ; b r e a k; }
}
Pointeurs sur fonctions
Est-il possible de manipuler des fonctions comme des variables ? Oui, au travers d’un pointeur de fonction. Int´erˆet :
I param´etrer un appel `a une fonction
I passer une fonction en argument `a une autre fonction (exemple : “map” de SCHEME)
Pointeurs sur fonctions
D´eclaration :
t y p e v a l e u r d e r e t o u r (∗ n o m d e v a r i a b l e ) ( t y p e 1 a r g 1 , . . . ) ;
Utilisation :
(∗ n o m d e v a r i a b l e ) ( a r g 1 , . . . ) ; n o m d e v a r i a b l e ( a r g 1 , . . . ) ;
Pointeurs sur fonctions : exemple
v o i d f 0 (v o i d) { . . . } v o i d f 1 (v o i d) { . . . } v o i d f 2 (v o i d) { . . . }
t y p e d e f v o i d (∗f o n c t i o n ) (v o i d) ; f o n c t i o n m e s f o n c t i o n s [ 3 ] ={f 0 , f 1 , f 2}; i n t main (v o i d) {
u n s i g n e d i n t n ;
p r i n t f ( ” E n t r e r un numero s t r i c t e m e n t i n f e r i e u r a 3 : ” ) ; s c a n f ( ” %d” ,&n ) ;
(∗m e s f o n c t i o n s [ n ] ) ( ) ; }
Pointeurs sur fonctions : applications
Parcours des arbres ou des listes : donner `a la fonction de parcours de la liste le pointeur sur la fonction `a appliquer (´equivalent au
“map” de SCHEME...).
Exemple : application d’une fonction `a chaque ´el´ement d’un tableau d’entiers :
t y p e d e f v o i d (∗f o n c t i o n S u r E n t i e r ) (i n t) ;
v o i d map ( f o n c t i o n S u r E n t i e r f , i n t ∗t a b l e a u , i n t t a i l l e ) { u n s i g n e d i n t i ;
f o r ( i =0; i<t a i l l e ; i ++) { f ( t a b l e a u [ i ] ) ;
} }
v o i d p r i n t i n t (i n t i ) {
p r i n t f ( ‘ ‘ E l e m e n t : %d\n ’ ’ , i ) ; }
i n t main (v o i d) {
i n t t a b [ 1 0 ] ={0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9}; map ( p r i n t i n t , tab , 1 0 ) ;
}
Pointeurs g´en´eriques
Pointeurs g´en´eriques : rappel
pointeurs void *.
I Pointeur “non typ´e”
I N´ecessite d’ˆetre cast´e en un type d´efini avant de pouvoir ˆetre d´er´ef´erenc´e.
I Permet une manipulation de pointeurs sans savoir sur quel type de donn´ees ils pointent (c’est ce que renvoie malloc).
Pointeurs g´en´eriques : utilisation
Exemples d’utilisation
i n t
c o m p a r e i n t (c o n s t v o i d ∗a , c o n s t v o i d ∗b ) {
c o n s t i n t ∗da = (c o n s t i n t ∗) a ; c o n s t i n t ∗db = (c o n s t i n t ∗) b ; r e t u r n (∗da > ∗db ) − (∗da < ∗db ) ; }
i n t main (v o i d) { u n s i g n e d i n t i ;
i n t t a b [ 1 0 ] ={3 , 1 , 0 , 4 , 2 , 8 , 6 , 9 , 7 , 5}; p r i n t f ( ” Avant :\n” ) ;
map ( p r i n t i n t , tab , 1 0 ) ;
q s o r t ( tab , 1 0 , s i z e o f(i n t) , c o m p a r e i n t ) ; p r i n t f ( ” A p r e s :\n” ) ;
map ( p r i n t i n t , tab , 1 0 ) ; }
Pointeurs g´en´eriques : ATTENTION
On ˆote des pr´erogatives au compilateur : il ne peut plus v´erifier les types `a la compilation !
i n t main (v o i d) { u n s i g n e d i n t i ;
d o u b l e t a b [ 1 0 ] ={3 . 1 , 1 . 2 , 0 . 5 , 4 . 3 , 2 . 1 , 8 . 2 , 6 . 1 , 9 . 4 , 7 . 2 , 5 . 1}; p r i n t f ( ” Avant :\n” ) ;
mapdouble ( p r i n t d o u b l e , tab , 1 0 ) ;
q s o r t ( tab , 1 0 , s i z e o f(d o u b l e) , c o m p a r e c h a r ) ; p r i n t f ( ” A p r e s :\n” ) ;
mapdouble ( p r i n t d o u b l e , tab , 1 0 ) ; }
Pointeurs g´en´eriques : ATTENTION
R´esultat : pas de probl`eme `a la compilation...
Avant:
Element : 3.100000 [...]
Element : 5.100000 Apr`es:
Element : 3.100000 Element : 2.100000 Element : 9.400000 Element : 7.200000 Element : 0.500000 Element : 1.200000 Element : 4.300000 Element : 8.200000 Element : 6.100000 Element : 5.100000
Pointeurs g´en´eriques : ATTENTION
A utiliser avec mod´eration, au risque de rendre le d´eveloppement` et surtout le debogage tr`es fastidieux ! ! Peut ˆetre remplac´e par l’utilisation d’un type qui sera d´efini par l’utilisateur (ex : TVal dans les TD/TME).
Le C++ contient des extensions permettant de g´erer cela plus proprement : “template”. Les templates permettent de param´etrer une fonction ou une classe par un type de donn´ee. Le compilateur pourra v´erifier l’exactitude du code.
Pointeurs sur fonctions et pointeurs g´en´eriques : applications
Fonction de tri g´en´erique :qsort(d´efinie dans stdlib.h).
v o i d q s o r t (v o i d ∗t a b l e , s i z e t nmemb , s i z e t s i z e , i n t (∗compar ) (c o n s t v o i d ∗, c o n s t v o i d ∗) ) ;
I tableest l’adresse du tableau `a trier I nmembest le nombre d’´el´ements I sizeest la taille d’un ´el´ement en octets
I comparest une fonction de comparaison utilisable pour comparer les ´el´ements du tableau
Pointeurs sur fonctions et pointeurs g´en´eriques : applications
Utilisation :
#i n c l u d e <s t d l i b . h>
#i n c l u d e <s t d i o . h>
#d e f i n e TABSIZE 10
i n t compare (c o n s t v o i d ∗a , c o n s t v o i d ∗b ) { i n t aa =∗((i n t ∗) a ) ;
i n t bb =∗((i n t ∗) b ) ; r e t u r n aa−bb ; }
Pointeurs sur fonctions & pointeurs g´en´eriques : applications
Utilisation :
i n t main (v o i d) { i n t t a b [ TABSIZE ] ; u n s i g n e d i n t i ;
f o r ( i =0; i<TABSIZE ; i ++) { t a b [ i ]= r a n d ( ) ;
p r i n t f ( ” t a b [%d]=%d\n” , i , t a b [ i ] ) ; }
q s o r t ( tab , TABSIZE ,s i z e o f(i n t) , compare ) ; p r i n t f ( ”\n\n A p r e s t r i :\n” ) ;
f o r ( i =0; i<TABSIZE ; i ++) {
p r i n t f ( ” t a b [%d]=%d\n” , i , t a b [ i ] ) ; }
}