Version 28 (modified by 18 years ago) (diff) | ,
---|
TME2 : Langage C: étude de cas sur les tables de hachage
Objectif
Pour développer une application en C, vous devez savoir:
- Ecrire un programme C en respectant des conventions d'écriture.
- Compiler en plusieurs fichiers objet et construire une librairie.
- Ecrire un Makefile.
- Debugger en utilisant gdb ou xgdb.
- Faire des mesures de performances avec gprof.
- Ecrire un man sur l'outil.
L'objectif de ce TME est double :
- Il doit d'une part vous permettre de complêtez l'auto-évaluation de vos connaissances des outils de developpement C que vous avez commencée dans le précédent TME, en vous posant des questions auxquelles vous devriez savoir répondre. Si ce n'est pas le cas, vous devez trouver les réponses dans les documentations (man, web), ou auprès de vos camarades.
- Il introduit de nouveaux outils permettant l'indentation automatique d'un programme source (outil indent), la constructtion d'une bibliothèque C (outil ar), ou l'écriture d'une documentation (outil man).
Il vous offre également un modèle de programme, avec Makefile et man pour vos futurs développements.
Travail demandé
- Vous devez commencer par copier sur votre compte le répertoire :
cp -rp /users/enseig/encadr/cao/tme2 ~/cao/tme2
Ce répertoire contient un programme utilisant une table de hachage. Le travail demandé comporte deux phases. Dans un premier temps vous devez analyser le code fourni, et rédiger des réponses aux questions portant sur ce code. Dans un deuxième temps, vous devrez modifier ce programme, pour introduire de nouvelles fonctionnalités.
Le programme fourni compte le nombre de mots d'un fichier texte et indique le nombre total de mots dans le fichier et le nombre de mots différents. Vous devrez modifier ce programme de façon à ce qu'il indique, pour chaque mot:
- le nombre d'occurences
- les numéros de toutes les lignes où il est présent
Vous donnerez également des statistiques sur l'usage des tables de hachage:
- taux de remplissage.
- moyenne du nombre de comparaisons nécessaire lors de la recherche d'un mot
Description des sources et principe des tables de hachage
-
Makefile .......................
description du processus de construction de l'exécutable. -
main.c, main.h .................
programme principal source et déclaration. -
count.c, count.h ...............
algorithme de parcours d'un fichier texte en vue de comptage. -
hte.c, hte.h ...................
fonction de création des tables de hachage et déclaration de toutes fonctions de gestion. -
dico.c, dejavu.c, namealloc.c ..
fonctions de gestion des tables pour trois types d'usage -
man1/tool.1 ....................
fichier au format man
Une table de hachage est une structure de données permettant de stocker des ensembles d'éléments, où chaque élément est un couple de la forme (clé, valeur). Le plus souvent la clé est une chaîne de caractères. La valeur peut être un nombre ou une structure de données quelconque. Le principal objectif de cette structure est d'accélérer la recherche d'un élément par sa clé.
Pour représenter un ensemble de couple (clé, valeur) la méthode la plus simple consiste à les réunir dans une liste chainée. La recherche d'un élément se fait alors par un parcours de la liste, donc de l'ensemble des éléments. Cette solution n'est évidemment pas satisfaisante si le nombre d'élément est grand.
Pour accélérer la recherche, on créé un tableau de listes chainées. Le nombre de cases de ce tableau doit être de l'ordre de grandeur du nombre maximal d'éléments que l'on souhaite gérer. Si on veut gérer 1000 éléments, il faut un tableau d'environ 1000 cases. On définit ensuite une fonction, que l'on nomme fonction de hachage, donnant un index de case à partir de la clé d'un élément. Un élément doit être rangé dans la liste chainée associée à la case définie par la fonction de hachage appliquée sur la clé de cet élément.
Pour que cette méthode soit efficace, il faut que les éléments se répartissent aussi uniformément que possible dans les différentes cases de la table. Il existe plusieurs méthodes de calcul de l'index, nous vous donnons celle proposé par Donald Knuth. Dans la pratique, il n'est pas possible d'éviter les collisions. Cela signifie que deux éléments de clés différentes peuvent avoir le même index de hachage.
En résumé, pour ranger 1000 éléments, on fabrique un tableau de 1000 listes chainées avec l'espoir que la plupart des listes ne contiendront qu'un seul élément et qu'il n'y aura qu'un tout petit nombre de listes contenant plus d'un élément.
La propriété principale d'une table de hachage est que si la taille de la table est du même ordre de grandeur que le nombre d'éléments présents, le temps de recherche est en O(1), c'est à dire indépendant du nombre d'éléments, (même pour un million d'éléments). Les deux principales actions sont l'ajout d'un élément (add) et la recherche d'un élément (get).
- La fonction get() prend en paramètre la clé de l'élément recherché. Si l'élément existe, elle rend la valeur associée.
- La fonction add() prend en paramètre le couple (clé, valeur). Si l'élément existe, elle change sa valeur, sinon elle créé l'élément.
Questions sur le code fourni
Le Makefile
Les premières questions portent sur le fichier attachment:"Makefile"
- Completez la liste des dépendances pour les cibles :
main.o ... namealloc.o
. - Réécrivez les commandes en utilisant les variables automatiques :
$@ $< $^
$@
: désigne le fichier cible d'une règle.$<
: désigne le premier fichier de la liste des fichiers source d'une règle.$^
: désigne la liste des fichiers source d'une règle.
- Donnez une raison à la definition des commandes et paramètres au début du Makefile
- A quoi servent les options -p, -g, -wall, -werror, -ansi ?
- Comment demander l'optimisation maximale du compilateur ?
- L'option -p est présente dans LDFLAGS et CFLAGS, pourquoi n'est-ce pas le cas de -g ?
- Que fait la regle indent ? quelle est la signification des flags utilisés par le programme indent ?
Le programme main
Les questions suivantes portent sur le programme principal attachment:main.c
- A quoi sert chaque include ?
- Pourquoi a-t-on un fichier main.h ?
- Expliquez le fonctionnement de la fonction getopt (
man 3 getopt
)
Ajoutez l'option -h qui affiche l'usage du programme et un petit texte de description du comportement (très court, c'est juste pour l'exercice).
Vous ajouterez plus tard l'option -s qui demande les statistiques d'usage de la tables de hachage.
- A quoi sert l'appel de return a la fin de la fonction main() ?
- Pourquoi y-a-t-il exit() a la fin de la fonction usage() ?
- Qu'est ce qu'un appel systeme, en voyez-vous dans ce fichier, si oui lesquels,
citez en d'autres.
- Quelle precaution doit on prendre lors de leur utilisation ?
- Ou sont definiies les fonctions standards ?
- Qu'est-ce qu'un filtre unix ?
- Que faut-il faire pour transformer ce programme en filtre ?
et sur le fichier de prototype associé attachment:main.h
- A quoi servent les lignes les 2 premières lignes et la dernière ?
- Pourquoi inclure
stdio.h
ici ?
Les fonctions de bases de la tables de hachage
Questions sur les déclaration du fichier attachment:hte.h
- Les types
hte_item_t
ethte_data_t
sont des structures dont le contenu n'est pas défini ici.
Leur contenu n'est pas défini non plus dans le fichier
hte.c
, en revanche il est défini dans les fichiersdico.c
,dejavu.c
etnamealloc.c
Quelle est alors la contrainte d'usage de ces types dans le fichierhte.c
? Quel est l'intérêt de cette écriture ?
- Dans la définition des prototypes de fonctions, le nom des paramètre est-il nécessaire ? si non pour les mettre ?
et sur les fichiers de créations des tables attachment:hte.c
- Faîte un dessin représentatif de la valeur de la variable locale
root
de la fonctionhte_create
sinb_list==10
juste avant l'appel return. - La fonction
hte_hash
peut-elle provoquer une erreur de segment ? Comment y remedier proprement ? - Dans la fonction
hte_create
comment fait-on pour tester le retour demalloc
? à quoi celà sert-il ?
Le service dictionnaire
Le fichier attachment:dico.c rassemble les fonctions d'accès à une table de hachage utilisé comme dictionnaire.
- Le type
hte_data_t
n'est pas défini ici. Est-ce grave ? Où devra-t-il être défini ? - Pourquoi teste-on key dans la fonction
hte_get
? - Pourquoi définit on la structure
hte_item_s
ici ? - Dans la structure
hte_item_s
le champ KEY est un tableau de taille indéfinie.
Quel différence y aurait-il avec une définition du type
char *KEY
?
- A quoi sert la fonction
perror
?
Les autres services possibles
Nous vous proposons deux autres services possibles des tables de hachage dans les fichiers, attachment:dejavu.c et attachment:namealloc.c Le premier, dejavu, permet de répondre à la question "ai-je déjà vu cette chaine de caractères". Le second, namealloc, permet de garantir que si deux chaines de caractères sont identiques alors elles seront rangées à la même adresse. Vous n'utiliserez pas ses services au cours de ce TME. Cependant, ils vous seront utiles lors des TME futurs. Vous pourrez faire alors une édition de liens avec la bibliothèque libhte.a. Pour aujourd'hui, contentez-vous de jeter un coup d'oeil par simple curiosité.
L'utilisation du dictionnaire pour compter des occurences de mots
Le code se trouve dans le fichier attachment:count.c
- Le mot clé static est utilisé de trois manières différentes. Quel est son effet ?
Evolution du programme
La fonction count fourni permet de faire de détecter les mots à occurences multiples. Nous souhaitons qu'il indique en plus les numéros de lignes où ces occurences apparaissent.
Attachments (11)
- tool.1 (4.7 KB) - added by 18 years ago.
- Diapositive1.jpg (17.9 KB) - added by 18 years ago.
- Makefile (590 bytes) - added by 18 years ago.
- count.c (2.4 KB) - added by 18 years ago.
- count.h (282 bytes) - added by 18 years ago.
- dico.c (3.5 KB) - added by 18 years ago.
- dico.h (1.9 KB) - added by 18 years ago.
- hash.c (540 bytes) - added by 18 years ago.
- hash.h (85 bytes) - added by 18 years ago.
- main.c (1.8 KB) - added by 18 years ago.
- main.h (176 bytes) - added by 18 years ago.
Download all attachments as: .zip