• Aucun résultat trouvé

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 ( ct 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 ( ct 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 ] ) ; }

}

Documents relatifs