Partie 1 - Bases de C¶
Pour l’organisation des fichiers (dossier p1/) et les options de compilation, voir les consignes.
Pour l’utilisation de valgrind (et de l’option -g), voir la page Outils.
p1e1 : pointeurs simples et pointeurs sur tableaux¶
Écrire une fonction division_et_reste qui effectue la division entière de deux entiers positifs a et b :
void division_et_reste(int a, int b, int *quotient, int *reste);
La fonction ne retourne rien, mais stocke le quotient et le reste dans deux variables passées par adresse.
Écrire une fonction min_et_max qui recherche, dans un tableau d’entiers de taille taille, le plus petit et le plus grand élément :
void min_et_max(const int *tab, int taille, int *min, int *max);
Les résultats seront renvoyés par deux variables passées par adresse.
Tester avec a = 17, b = 5 et le tableau {5, 9, 2, 7, 3} déclaré dans le main.
Résultat attendu :
17 / 5 = 3, reste = 2
Tableau : 5, 9, 2, 7, 3
- min : 2
- max : 9
p1e2 : pointeurs et structures¶
Définir un type Duration (typedef struct { ... } Duration;) contenant 3 entiers non signés (unsigned int) : heures, minutes, secondes.
Dans le main, déclarer et initialiser :
d1à 0h150min500s (volontairement non normalisée, donnera 2h38min20s une fois normalisée) ;d2à 5h35min58s ;d3à 2h45min06s.
Implémenter :
void print_duration(const Duration *d), qui affiche la durée au format hh:mm:ss (ex. 05:35:58, 02:45:06). Utiliser%02u: au moins 2 chiffres, complétés par des 0 (une valeur plus grande, comme 150, est affichée en entier) ;void normalize_duration(Duration *d), qui normalise la durée (ex. 75s → 1min15s, 70min130s → 1h12min10s). Les heures ne sont pas limitées à 24 ;void add_duration(const Duration *d1, const Duration *d2, Duration *result), qui calculeresult = d1 + d2, puis normaliseresult.
Tester ces fonctions avec d1, d2, d3 et une variable Duration sum pour stocker les résultats.
Résultat attendu :
d1 brut : 00:150:500
d1 normalisé : 02:38:20
d2 : 05:35:58
d3 : 02:45:06
d1 + d2 = 08:14:18
d2 + d3 = 08:21:04
d1 + d3 = 05:23:26
p1e3 : création de tableau dynamique¶
Écrire un programme C qui :
demande une taille de tableau (qui doit être
> 0, sinon le programme se termine) ;alloue dynamiquement un tableau d’entiers de cette taille (voir Gestion mémoire / Allocation dynamique) ;
remplit le tableau avec des valeurs aléatoires (voir Aléatoire) comprises dans l’intervalle
[50, 100[;affiche le contenu du tableau avec une fonction
void print_table(const int *tab, int n).
Vérifier qu’il n’y a pas de fuites mémoire (voir Analyse à l’exécution).
Résultat attendu :
$ gcc -std=c2x -Wall -Wextra -pedantic -g p1/p1e3.c && valgrind ./a.out
... affichage de valgrind
Taille du tableau (>0) : 20
Contenu du tableau : 66 81 75 64 77 73 66 83 59 99 72 83 72 65 98 69 59 99 79 93
... affichage de valgrind sans fuites mémoire
p1e4 : modification de tableau dynamique¶
Reprendre le code précédent, puis modifier la taille du tableau (en gardant les données initiales) en fonction d’une nouvelle taille saisie par l’utilisateur, à l’aide de realloc.
Si la nouvelle taille est plus grande, remplir les nouvelles cases avec des valeurs aléatoires.
Une taille <= 0 termine le programme.
Afficher ensuite le nouveau tableau.
Vérifier qu’il n’y a pas de fuites mémoire (voir Analyse à l’exécution).
Résultat attendu :
$ gcc -std=c2x -Wall -Wextra -pedantic -g p1/p1e4.c && valgrind ./a.out
... affichage de valgrind
Taille du tableau (>0) : 20
Tableau initial : 57 75 71 71 99 91 73 73 81 87 65 76 94 73 78 80 51 56 67 79
Nouvelle taille (>0) : 10
Nouveau tableau : 57 75 71 71 99 91 73 73 81 87
... affichage de valgrind sans fuites mémoire
$ gcc -std=c2x -Wall -Wextra -pedantic -g p1/p1e4.c && valgrind ./a.out
... affichage de valgrind
Taille du tableau (>0) : 10
Tableau initial : 51 72 71 50 66 81 55 96 55 83
Nouvelle taille (>0) : 20
Nouveau tableau : 51 72 71 50 66 81 55 96 55 83 90 90 92 50 76 66 87 56 98 58
... affichage de valgrind sans fuites mémoire
p1e5 : tableau de chaînes de caractères¶
Écrire un programme C qui :
demande un nombre
nde chaînes ;alloue dynamiquement un tableau de
nchaînes de caractères ;le remplit avec des chaînes saisies dans le terminal : lire chaque chaîne dans un tampon de taille fixe (ex.
char buf[256]), puis allouerstrlen(buf) + 1octets et y copier la chaîne ;une fois les
nchaînes enregistrées, parcourt le tableau, regarde si la chaîne"coucou"est présente et affiche l’indice de sa première occurrence ;pour finir, affiche les chaînes du tableau.
Lire n avec fgets puis atoi : mélanger scanf et fgets laisse un \n dans le tampon d’entrée, que le fgets suivant lirait comme une ligne vide.
Fonctions utiles (inclure <string.h> et <stdlib.h>, ainsi que <stdbool.h> pour read_line) :
strlen(chaine)pour obtenir la longueur d’une chaîne ;strcpy(destination, source)pour copier une chaîne ;fgets(chaine, taille_max, flux), qui, contrairement àscanf, lit aussi les espaces. Dans notre cas, le flux d’entrée (flux) serastdin;atoi(chaine)pour convertir une chaîne en entier ;int strcmp(const char *s1, const char *s2)pour comparer deux chaînes (retourne 0 si elles sont égales).
Voici une fonction read_line qui lit une entrée utilisateur et retire le \n :
/**
* @brief Lit une ligne (y compris les espaces) dans buffer et retire le '\n'
*
* @param buffer tableau dans lequel écrire les données
* @param taille nombre maximal de caractères (taille du tableau)
* @return true si la lecture a réussi, false sinon (fin de l'entrée ou erreur)
*/
bool read_line(char *buffer, int taille) {
if (fgets(buffer, taille, stdin) == NULL) {
buffer[0] = '\0';
return false;
}
buffer[strcspn(buffer, "\n")] = '\0';
return true;
}
Vérifier qu’il n’y a pas de fuites mémoire (voir Analyse à l’exécution).
Résultat attendu :
$ gcc -std=c2x -Wall -Wextra -pedantic -g p1/p1e5.c && valgrind ./a.out
... affichage de valgrind
Nombre de chaînes : 3
Chaîne 1 : hello
Chaîne 2 : hey
Chaîne 3 : hej
À la recherche de coucou...
coucou n'est pas dans le tableau
Affichage final :
[0] hello
[1] hey
[2] hej
... affichage de valgrind sans fuites mémoire
$ valgrind ./a.out
... affichage de valgrind
Nombre de chaînes : 3
Chaîne 1 : hello
Chaîne 2 : coucou
Chaîne 3 : hej
À la recherche de coucou...
coucou est trouvé à l'indice 1
Affichage final :
[0] hello
[1] coucou
[2] hej
... affichage de valgrind sans fuites mémoire
p1e6 : création de matrice dynamique¶
Écrire un programme C qui :
demande à l’utilisateur un nombre de lignes
rowset de colonnescols(strictement positifs) ;alloue dynamiquement une matrice (
int **) d’entiers de taillerows × colsà l’aide d’une fonctionint **alloc_matrix(size_t rows, size_t cols);la remplit avec des valeurs aléatoires dans l’intervalle
[20, 60[;l’affiche avec une fonction
void print_matrix(int **matrix, size_t rows, size_t cols), qui met une tabulation entre chaque élément et revient à la ligne à chaque nouvelle ligne ;calcule avec deux fonctions
int *sum_rows(int **matrix, size_t rows, size_t cols)etint *sum_columns(int **matrix, size_t rows, size_t cols)les sommes par ligne et par colonne :sum_rowsrenvoie un tableau derowssommes (une par ligne),sum_columnsun tableau decolssommes (une par colonne), tous deux alloués dynamiquement. Afficher ces sommes dans lemain;libère la matrice avec une fonction
void free_matrix(int **matrix, size_t rows), sans oublier les tableaux renvoyés parsum_rowsetsum_columns.
Vérifier qu’il n’y a pas de fuites mémoire (voir Analyse à l’exécution).
Résultat attendu :
$ gcc -std=c2x -Wall -Wextra -pedantic -g p1/p1e6.c && valgrind ./a.out
... affichage de valgrind
Nombre de lignes (>0) : 5
Nombre de colonnes (>0) : 3
Matrice 5x3 :
41 54 24
51 57 23
41 32 36
28 39 47
23 33 30
Somme des lignes :
119 131 109 114 86
Somme des colonnes :
184 215 160
... affichage de valgrind sans fuites mémoire