Enonce TME1 Et 2
Enonce TME1 Et 2
Enonce TME1 Et 2
Ce mini projet est à réaliser pendant les deux premières séances de TME. Il va vous permettre de
découvrir deux techniques qui seront utiles pour l’ensemble des TME du semestre (et notamment
pour le projet final). Plus précisément, le premier exercice traite de techniques de débogue, tandis
que deuxième exercice est l’occasion d’étudier une façon d’évaluer la vitesse d’un programme et de
comparer les vitesses de plusieurs programmes. Les outils fournis pour ces deux exercices ne sont
pas obligatoires et pourront être remplacés par des outils équivalents. Les fichiers nécessaires à la
réalisation de ce TME peuvent être récupérés sur moodle.
La bonne compilation d’un programme écrit en C n’assure malheureusement pas son bon fonction-
nement. Il existe souvent des erreurs qui ne sont révélées qu’au moment de l’exécution et qu’il faut
trouver. Parmi ces erreurs, la plus courante est l’“erreur de segmentation” qui survient souvent lors de
la manipulation de tableaux, ou de pointeurs. L’exercice suivant vous présente l’utilisation d’un outil
de débogue qui vous permettra la plupart du temps de localiser vos erreurs.
Q 1.1 À la lecture du code, qu’est censé faire le programme d’après vous ? Compilez le avec la
commande suivante gcc -o tme1 exo1p1 tme1 exo1p1.c puis lancer le. Que se passe-t-il ?
Q 1.2 Pour trouver l’erreur dans le programme, nous allons utiliser gdb, un outil de débogue très
pratique. Recompilez votre programme avec la commande suivante : gcc -ggdb -o tme1 exo1p1
tme1 exo1p1.c. L’option -ggdb permet d’inclure dans l’exécutable des informations supplémentaires
qui facilite le débogue. Voici ci-dessous un résumé des commandes importantes sous gdb :
LU2IN006 – Structures de données page 2
19 }
20
21 int main ( void ) {
22 Adresse * maison = creer_adresse (12 , ” manoeuvre ” , 15670) ;
23
24 printf ( ” A d r e s s e c o u r a n t e : %d r u e %s %d F r a n c e \n” , maison - > numero , maison - > rue ,
maison - > code_postal ) ;
25
26 return 0;
27 }
Q 1.4 À la lecture du code, qu’est censé faire le programme d’après vous ? Compilez le programme et
lancez le. Que se passe-t-il ?
Q 1.5 Recompilez le programme en activant l’option de débogue -ggdb et lancez l’outil gdb sur
l’exécutable résultant. Créez un point d’arrêt au niveau de la ligne 15 du fichier avec l’instruction
break 15. Faites de même avec la ligne 16 avec l’instruction break 16.
— Lancez l’exécution du programme avec la commande run. Le programme va s’arrêter au premier
point d’arrêt (ligne 15). Afficher la valeur de new->rue avec l’instruction print new->rue. Que
voyez-vous ?
— Continuer l’exécution pour arriver au deuxième point d’arrêt (ligne 16). Que constatez-vous ?
À quel moment survient l’erreur ?
— Expliquez la cause de l’erreur et proposer une correction.
29 printf ( ” ] \ n” ) ;
30 }
31
32 int main () {
33 Tableau * t ;
34 t = initTableau (100) ;
35 ajouterElement (5 , t ) ;
36 ajouterElement (18 , t ) ;
37 ajouterElement (99999 , t ) ;
38 ajouterElement ( -452 , t ) ;
39 ajouterElement (4587 , t ) ;
40 affichageTableau ( t ) ;
41 free ( t ) ;
42 }
Q 1.6 À la lecture du code, qu’est censé faire le programme d’après vous ? Compilez le programme et
lancez le. Que se passe-t-il ?
Q 1.7 Quels sont les problèmes de ce programme ?
Q 1.8 Nous allons utiliser l’outil valgrind pour repérer les fuites mémoires éventuelles. Lancer la
commande valgrind --leak-check=yes ./tme1 exo1p3. Que constatez-vous ? À quoi correspond
les 400 bytes relevés par l’outil ?
Q 1.9 Proposez une correction du programme et vérifiez qu’il n’y a plus de fuite mémoire (en utilisant
l’outil valgrind).
Dans cet exercice, il s’agit d’étudier la complexité temporelle de différents algorithmes permettant de
résoudre un même problème. On s’intéressera à la complexité temps-pire cas, en donnant des bornes
avec la notation Landau O.
Dans cette partie, on s’intéresse à des algorithmes travaillant sur des tableaux à une dimension.
Q 2.1 Commencez par créer les fonctions suivantes :
1. une fonction alloue tableau permettant d’allouer la mémoire utilisée par un tableau T d’en-
tiers de taille n. Noter qu’il existe deux versions possibles pour cette fonction :
alloue tableau(int n); et alloue tableau(int **T, int n);.
Dans la première version, le tableau est retourné par la fonction, tandis que dans la seconde
version, on utilise le passage par référence. Justifier votre choix.
2. une fonction desalloue tableau permettant de désallouer la mémoire utilisée par un tableau
d’entiers de taille n.
3. une fonction remplir tableau permettant de remplir le tableau avec des valeurs entières
aléatoirement générées entre 0 et V (non-compris).
4. une fonction afficher tableau permettant d’afficher les valeurs du tableau.
LU2IN006 – Structures de données page 5
Q 2.2 On souhaite écrire un algorithme efficace permettant de calculer la somme des carrés des
différences entre les éléments du tableau pris deux à deux :
n X
X n 2
T (i) − T (j)
i=1 j=1
Q 2.3 L’objectif est maintenant de comparer les temps de calcul des deux algorithmes en faisant varier
la taille n du tableau. Pour mesurer le temps mis par le CPU pour effectuer une fonction fct(int n),
on peut utiliser le code suivant où temps cpu contient le temps CPU utilisé en secondes.
Pour analyser les résultats obtenus, on veut visualiser par des courbes les séries de temps obtenues. Pour
cela, on peut utiliser un logiciel extérieur lisant un fichier texte créé par le programme. Par exemple,
le logiciel gnuplot qui est utilisable en ligne sous linux permet de faire des courbes en lisant un fichier.
Pour notre problème, le fichier d’entrée de gnuplot, nommé ”sortie vitesse.txt”, est simplement la
donnée en lignes de n suivi des mesures de temps. On peut lancer gnuplot puis taper interactivement
les commandes pour lire le fichier ”sortie vitesse.txt”, ou bien utiliser une redirection de la forme
gnuplopt -p < commande.txt avec un fichier commande.txt du type :
L’option -p permet de garder la fenêtre du graphique ouverte. Ces lignes de commande créent sur
le disque le fichier postscript contenant deux courbes sur un même graphique. Justifiez les courbes
obtenues avec votre programme en fonction de la complexité pire-cas attendue.
LU2IN006 – Structures de données page 6
Dans cette partie, on souhaite écrire des algorithmes efficaces pour tester si une matrice contient uni-
quement des valeurs différentes, puis écrire des programmes pour réaliser le produit de deux matrices.
Q 2.4 Commencez par créer les fonctions suivantes :
1. une fonction alloue matrice permettant d’allouer la mémoire utilisée par une matrice entière
carrée de taille n x n.
2. une fonction desalloue matrice permettant de désallouer la mémoire utilisée par une matrice
carrée.
3. une fonction remplir matrice permettant de remplir une matrice carrée avec des valeurs
entières aléatoirement générées entre 0 et V (non-compris).
4. une fonction afficher matrice permettant d’afficher les valeurs d’une matrice carrée.
Q 2.5 On souhaite écrire un programme permettant de vérifier efficacement que tous les éléments de
la matrice ont des valeurs différentes.
1. Écrivez un premier algorithme de complexité O(n4 ).
2. Écrivez un second algorithme de meilleure complexité dans le cas où la borne maximale V sur
les valeurs contenues dans la matrice est connue. Pour obtenir une meilleure complexité, utilisez
un tableau de taille V alloué en mémoire.
3. Comparez les temps de calcul des deux algorithmes et représentez les courbes obtenues en
fonction du nombre n d’éléments de la matrice. Analysez les résultats en justifiant les courbes
obtenues en fonction de la complexité pire-cas attendue.
Q 2.6 Dans cette question, on souhaite comparer deux algorithmes permettant d’effectuer le produit
de deux matrices.
1. Écrivez un premier algorithme de complexité O(n3 ).
2. Écrivez un second algorithme avec moins d’instructions, en tenant compte du fait que le produit
concerne une matrice triangulaire supérieure et une matrice triangulaire inférieure.
Rappel : une matrice m est triangulaire supérieure si m[i][j] = 0 pour tout i > j, et triangulaire
inférieure si m[i][j] = 0 pour tout i < j.
N.B. : Vous pouvez utiliser un struct pour créer le type MatTriangulaire (comme en TD),
mais ce n’est pas obligatoire.
3. Ce dernier algorithme est-il de meilleur complexité ? Justifiez votre réponse.
4. Comparez les temps de calcul des deux algorithmes et représentez les courbes obtenues en
fonction du nombre n d’éléments de la matrice. Justifiez les courbes obtenues en fonction de la
complexité pire-cas attendue.