INF3105 — Structures de données et algorithmes
Été 2024
UQAM
Département d'informatique

Laboratoire 1 : S'initier à C++ et aux outils du cours

Lectures préalables

Objectifs

  1. Apprivoiser un environnement de développement minimaliste en C++.
  2. Compiler en ligne de commande à l'aide de g++.
  3. Comprendre les messages d'erreur du compilateur.
  4. Créer un fichier Makefile
  5. Comprendre les erreurs dues à une mauvaise compilation.
  6. Résoudre un problème simple.

Tâches

Les démonstrateurs feront avec vous les tâches 1 à 3. Pour ne rien rater, soyez à l'heure!

L'Environnement minimaliste

  1. Ouvrez un terminal.
  2. Faites les commandes suivantes.
  3. wget http://cria2.uqam.ca/INF3105/lab1/lab1.zip
    unzip lab1.zip
    
  4. Ouvrez le fichier lab1.cpp dans un éditeur texte (ex.: gedit).

Première compilation

Tapez les commandes suivantes :

$ g++ lab1.cpp
$ ls
$ ./a.out
$ rm a.out
$ g++ -o lab1 lab1.cpp
$ ls
$ ./lab1

Fonction main2()

A. Éditez lab1.cpp :

  1. Décommentez la ligne #include "point.h".
  2. Supprimez la fonction main() ou renommez-la main1().
  3. Décommentez la fonction main2() et renommez-la en main().

B. Tapez la commande :

$ g++ -o lab1 lab1.cpp

C. Que se passe-t-il ?

D. Essayer de comprendre les messages d'erreurs.

E. Faites les commandes :

$ g++ -o lab1 lab1.cpp point.cpp
$ ls
$ ./lab1

F. Que se passe-t-il ?

G. Faites les commandes :

$ g++ -c lab1.cpp
$ ls
$ g++ -c point.cpp
$ ls
$ g++ -o lab1 lab1.o point.o
$ ls
$ ./lab1

H. Que se passe-t-il ?

Création d'un fichier Makefile

La compilation des travaux pratiques dans le cours se fait à l'aide d'un Makefile. Un fichier Makefile décrit comment construire (compiler) un projet avec la commande make. Pour le cours INF3105, vous n'avez pas besoin d'être un expert Makefile. Dans la plupart des cas, un fichier Makefile vous sera fourni et vous n'aurez pas à le modifier. Toutefois, il est souhaitable d'avoir une connaissance générale sur les Makefile. Le cours INF3135 ira plus en profondeur sur ce sujet.

A. Ouvrez le fichier Makefile dans l'éditeur et analysez-le. Ne le modifiez pas tout de suite.

OPTIONS = -g -O0 -Wall

# Syntaxe : cible : dépendance1 dépendance2 ...
# Ensuite, la ou les ligne(s) débutant par une tabulation (\t) donne les commandes pour construire une cible
lab1: lab1.cpp
    g++ $(OPTIONS) -o lab1 lab1.cpp


clean :
    rm -f *.o
    rm -f lab1
    rm -f *~

B. Tapez la commande :

$ make

C. Que se passe-t-il ?

D. Éditez le fichier Makefile pour qu'il contienne :

#Option -g pour avoir les infos de débogage
OPTIONS = -g -O0 -Wall

#Option -O3 pour le code optimisé
#OPTIONS = -O3 -Wall

# Syntaxe : cible : dépendance1 dépendance2 ...
# Ensuite, la ou les ligne(s) débutant par une tabulation (\t) donne les commandes pour construire une cible
lab1: lab1.o point.o
    g++ $(OPTIONS) -o lab1 lab1.o point.o

lab1.o: lab1.cpp
    g++ $(OPTIONS) -c -o lab1.o lab1.cpp

point.o: point.cpp
    g++ $(OPTIONS) -c -o point.o point.cpp

clean :
    rm -f *.o
    rm -f lab1

Attention : si vous faites un copier-coller, remplacez les 5 blocs de 4 espaces (devant les commandes g++ et rm) par une tabulation (caractère '\t'). Le programme make reconnaît les commandes par les tabulations.

E. Tapez les commandes :

$ make clean
$ make
$ make

F. Que se passe-t-il ?

Comprendre les dépendances dans le fichier Makefile (1)

  1. Ajoutez la date d'aujourd'hui dans le commentaire en haut du fichier point.cpp. L'objectif est uniquement de modifier le fichier afin de changer sa date de modification.
  2. Tapez la commande make
  3. Que se passe-t-il ? Quels sont les fichiers recompilés? Le fichier lab1.cpp ne devrait pas être compilé.
  4. Faites les commandes
  5. make clean
    ls
    make
    ls
    ./lab1
    
  6. Maintenant, remplacez toutes les occurrences de float par double dans les fichires point.h et point.cpp. Ne modifiez pas lab1.cpp.
  7. Faites les commandes :
  8. make  # ne faites pas make clean avant
    ./lab1
    
  9. Que se passe-t-il ? Avez-vous un bogue dans votre programme?
  10. Faites les commandes :
  11. make clean
    make
    ./lab1
    
  12. Que se passe-t-il ?
  13. Éditez le fichier Makefile pour ajouter point.h dans les dépendances de la cible point.o.
  14. ...
    point.o: point.cpp point.h
        g++ $(OPTIONS) -c -o point.o point.cpp
    ...
    
  15. Faites make.
  16. Modifiez point.h et enregistrez-le, puis faites make. Vous venez de régler un problème de dépendance.

Comprendre les dépendances dans le fichier Makefile (2)

1. Modifiez lab1.cpp : renommez main() en main2(), décommentez main3() et renommez main3() en main().

2. Faites les commandes :

make
./lab1

3. Notez le résultat affiché. Est-il conforme au programme? (oui).

4. Ajoutez une variable a dans la classe Point entre x et y :

  //private: // ne pas décommenter tout de suite
    double x;
    double a;
    double y;

5. Faites la commande make. Ne faites pas make clean.

6. Quels fichiers ont été recompilés?

7. Faites la commande ./lab1 et comparez le résultat avec l'étape 3. 

8. Que se passe-t-il? Est-ce que le résultat est bon? Tentez d'expliquer pourquoi.

9. Éditez le fichier Makefile pour ajouter point.h dans les dépendances de lab1.o :

lab1.o: lab1.cpp point.h
    g++ $(OPTIONS) -c -o lab1.o lab1.cpp

10. Faites la commande make.

Vous venez de régler un deuxième problème de dépendance. Il est important de comprendre que make ne fait que lire les dépendances qui lui sont spécifiées. Make ne les détecte pas automatiquement en analysant les sources. C'est celui qui écrit ou qui génère le fichier Makefile qui doit penser aux dépendances!

Heureusement, il y a des outils pour considérer les dépendances. Si cela vous intéresse, suivez le cours INF3135 – Construction et maintenance de logiciels ou lisez sur cmake et autoconf. Pour le cours INF3105, l'écriture de fichier Makefile à la main sera suffisante.

Lire et écrire un point

1. Éditez point.h pour décommenter la ligne « private » pour rendre x et y privés.
 
 Enlevez la variable a.

[...]
  private:
    double x;
    double y;
[...]
2. Éditez lab1.cpp :

3. Invoquez la commande make

4. Tapez ./lab1 puis appuyez sur [Entrée]. On vous demandera d'entrer un point : entrez (2,3), avec les parenthèses, puis faites [Entrée].

Implémentation de la fonction distance

1. Éditez point.cpp pour implémenter distance(). Vous pouvez trouver quelle est la fonction racine carrée en consultant la documentation à http://cppreference.com/.

2. Éditez lab1.cpp pour passer au main5().

3. Testez.

Chargement d'un nuage de points

1. Éditez lab1.cpp pour passer au main6().

2. C'est à votre tour d'écrire le code...     Ajoutez ce qu'il faut pour lire tous les points...

3. Tapez la commande :

./lab1 < nuage1.txt

Trouver les 2 points les plus près

Vous devez coder 2 boucles for

Votre programme doit afficher la distance entre les 2 points les plus près.

Tapez les commandes :

make
./lab1 < nuage1.txt

Mesure du temps d'exécution

1. Tapez les commandes :

time ./lab1 < nuage1.txt
time ./lab1 < nuage2.txt
time ./lab1 < nuage3.txt
time ./lab1 < nuage4.txt
time ./lab1 < nuage5.txt

2. Modifiez Makefile afin d'utiliser l'option -O3 du compilateur.

3. Tapez les commandes :

make clean
make
time ./lab1 < nuage1.txt
time ./lab1 < nuage2.txt
time ./lab1 < nuage3.txt
time ./lab1 < nuage4.txt
time ./lab1 < nuage5.txt

Observations?

Fin !