1. Erreurs à l'exécution.
Si un programme est correct syntaxiquement, il se peut néanmoins que le programme exécutable produit ne réalise pas les traitements escomptés, à cause d'erreurs appelées "erreurs à l'exécution", qui peuvent être de deux types différents :
- Soit un message plus ou moins explicite est affiché par le système, comme par exemple :
Dans ce cas, aucun résultat n'est fourni.
- Soit le programme exécutable fournit bien des résultats, mais ces résultats sont faux (pour détecter ce deuxième type d'erreurs, il faut en général tester le programme avec différents jeux de données).
Ces deux types d'erreurs sont souvent difficiles à détecter. Il existe des outils dont le rôle est d'aider le programmeur à les corriger, et qui s'appellent des "débogueurs" ("debuggers" en anglais).
2. Présentation du débogueur xxgdb.
Si l'on désire corriger une erreur à l'exécution dans un programme contenu dans le fichier fichier.c, il est conseillé d'utiliser le débogueur xxgdb, en procédant de la manière suivante :
- Compiler le programme, en utilisant la commande :
gcc -ansi -pedantic -Wall -Werror -g -o fichier fichier.c
qui produit le programme exécutable dans un fichier de nom fichier
L'oubli de l'option -g dans la commande de compilation rend l'utilisation du débogueur
xxgdb impossible.
- Lancer le débogueur xxgdb, à l'aide de la commande :
Une fenêtre s'affiche alors. Elle est constituée de trois parties :
- La partie supérieure affiche le programme source contenu dans le fichier fichier.c
- La partie centrale comporte un certain nombre de boutons correspondant à des commandes.
- La partie inférieure permet de taper des commandes (ou de cliquer sur les boutons de la partie centrale qui leur correspondent) ou de taper des données, lors de l'exécution du programme.
- À l'aide de la souris, positionner le curseur à chaque endroit du programme source où l'on souhaite que l'exécution s'arrête, puis cliquer sur le bouton break (un dessin représentant une main apparaît alors à l'endroit désiré). Cette étape s'appelle le "placement des points d'arrêt".
- Lancer l'exécution du programme, en cliquant sur le bouton run. L'exécution se déroule jusqu'au premier point d'arrêt rencontré (s'il faut taper des données au clavier, le faire).
- Faire afficher la valeur d'une ou de plusieurs variables en ce point d'arrêt, de l'une des deux manières suivantes :
- Soit en sélectionnant le nom de cette variable, à l'aide de la souris, en un endroit quelconque du programme, puis en cliquant sur le bouton print. Cela permet d'afficher la valeur de cette variable de manière ponctuelle.
- Soit en sélectionnant le nom de cette variable, à l'aide de la souris, en un endroit quelconque du programme, puis en cliquant sur le bouton display. Cela permet d'afficher la valeur de cette variable de manière permanente.
- Poursuivre l'exécution du programme, de l'une des trois manières suivantes :
- Soit jusqu'au prochain point d'arrêt, en cliquant sur le bouton cont.
- Soit jusqu'à l'instruction suivante (en rentrant dans le corps des fonctions, s'il y en a), en cliquant sur le bouton step.
- Soit jusqu'à l'instruction suivante (en ne rentrant pas dans le corps des fonctions), en cliquant sur le bouton next.
Remarque :
L'utilisation de ce débogueur est particulièrement recommandée pour la mise au point du projet du module 4.
Application :
3. Exercice 1.
Sur une matrice n x n, une "situation"
est composée de cases noires et de cases blanches. Les cases noires
indiquent une "situation de vie", et les cases blanches, une
"situation de mort". Pour une case m[i][j], on définit quatre
voisins :
m[i-1][j], m[i+1][j], m[i][j-1]
et m[i][j+1].
À chaque tour de jeu, on réexamine la situation des cases
de la grille selon les règles suivantes :
|
Quatre voisins noirs : "mort par étouffement".
Si la case est blanche, elle reste blanche.
Si la case est noire, elle devient blanche. |
|
Trois voisins noirs : aucun changement.
|
|
Deux voisins noirs : "naissance".
Si la case est noire, elle reste noire.
Si la case est blanche, elle devient noire.
|
|
Un voisin noir : aucun changement.
|
|
Zéro voisin noir : "mort par isolement".
Si la case est blanche, elle reste blanche.
Si la case est noire, elle devient blanche.
|
Écrire un programme qui, après avoir lu une configuration
initiale dans un fichier, demande à l'utilisateur le nombre d'étapes
qu'il veut visualiser, et les affiche une à une à l'écran.
Le problème peut être simplifié en prenant une matrice
(n+2)x(n+2) dont les contours sont toujours blancs.
En effet, comme les cases blanches n'interviennent pas dans la décision
de modification de l'état d'une case, cette bordure rajoutée
artificiellement permettra de ne pas faire de traitement particulier pour
les cases de bordure ou pour les cases d'angle de la matrice n
x n, qui sans cela ne posséderaient que deux ou trois
voisins chacune.
On pourra tester le programme produit sur la configuration initiale suivante :
Pour cela, à l'aide de l'éditeur de texte asedit, créer un fichier de texte, de nom etape0.txt, contenant 49 caractères B (pour "blanc") ou N (pour "noir"), et ne contenant ni caractère espace ni caractère retour-chariot, dans un ordre qui corresponde à la configuration du dessin ci-dessus, lue ligne par ligne.
On aura intérêt à utiliser les définitions
suivantes :
Il est demandé de ne pas utiliser de variable globale.
On écrira les fonctions suivantes :
- initialise : initialise la matrice, en mettant des
cases blanches sur tout le contour, et en lisant la couleur initiale de chacune des cases non situées
au bord dans le fichier etape0.txt (pour la manipulation des fonctions fopen, fscanf et fclose, cf. la deuxième séance de travaux pratiques).
- compte : retourne le nombre de voisins noirs d'une
case.
- change : calcule une nouvelle situation.
Pour l'écriture de cette fonction, il est absolument nécessaire de compter le nombre de voisins
noirs de chaque case en se référant à la situation précédente, et non à la situation courante.
- affiche : affiche une situation, par exemple sous
la forme suivante :
> tp03ex01
Combien d'étapes souhaitez-vous visualiser : 15
Etape : 0
B B B B B B N
B B B B B N N
B B B B N N N
B B B N N N N
B B N N N N N
B N N N N N N
N N N N N N N
Etape : 1
...
Etape : 15
N N N N N N N
N N B N B N N
N B N N N B N
N N N B N N N
N B N N B N N
N N B N N B N
N N N N N N N
>
|
4. Exercice 2.
Écrire un programme permettant de lire et de ranger par ordre croissant un ensemble de
TAILLE entiers.
On utilisera obligatoirement les déclarations suivantes :
#include <stdio.h>
#define TAILLE 10
...
int main(void)
{
int entiers[TAILLE];
int *pointeurs[TAILLE];
...
}
Le tableau entiers contient les entiers, alors que chaque case du tableau
pointeurs doit pointer sur la case du tableau entiers de
même indice. Une fois initialisé, le tableau entiers ne devra plus être modifié.
L'opération de tri sera donc effectuée par l'intermédiaire du tableau pointeurs.
On peut utiliser un algorithme de tri quelconque (toutefois, il est conseillé d'utiliser l'algorithme de tri
par substitution, qui est particulièrement simple à mettre en oeuvre).
On écrira les fonctions suivantes :
- void lire(int entiers[])
Cette fonction effectue l'initialisation du tableau entiers
- void initialiser(int entiers[],int *pointeurs[])
Cette fonction effectue l'initialisation du tableau pointeurs
- void trier(int *pointeurs[])
Cette fonction trie les cases du tableau pointeurs, de telle sorte
que sa première case pointe vers le plus petit entier, etc...
- void afficher(int *pointeurs[])
Cette fonction affiche les entiers par ordre croissant.
Ces pages ont été réalisées
par A. Crouzil, J.D. Durou et Ph. Joly.
Pour tout commentaire, envoyer un mail à
crouzil@irit.fr, à durou@irit.fr ou à Philippe.Joly@irit.fr.