TP1 - Rappels de C
L'objectif de ce TP est de se remettre dans le bain de la programmation C.
J'espère que ça va bien se passer !
Partie 1 - Structures, pointeurs et mémoire
Voici un fichier C minimal :
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
char ch = 'J';
printf("hello le %c\n", ch);
return EXIT_SUCCESS;
}
Explications `argc` / `argv` (Cliquez pour déplier)
int argc
représente le nombre d'items contenus dans argv
.
char **argv
représente les arguments passés au programme.
char **argv
est un pointeur de pointeurs de caractères...
Pas de panique : C'est donc un tableau de pointeurs char *
.
char *
est la façon dont on stocke des strings en C.
Les char *
n'embarquent pas leur taille comme métadonnée.
À la place, on met le caractère nul (\0
) en dernier caractère de la chaine.
Explications `printf()` (Cliquez pour déplier)
printf()
affiche du texte sur la sortie standard (/dev/stdout
)
Son premier argument est une chaine de caractères constante, const char *
. Ça veut dire que cette chaîne est hardcodée dans votre programme. C'est du "texte entre guillemets"
.
Elle peut contenir des placeholders pour des valeurs variables :
%d
pour unint
,%c
pour unchar
,%s
pour unchar *
,
Pour plus d'informations, tapez man printf
dans un terminal,
ou consultez-le en ligne.
Exercice 1.1
Écrivez un programme args.c
qui affiche les arguments passés au programme, un par ligne.
Exemple :
$ gcc args.c -o args # on compile le programme
$ ./args hello world # on lance le programme avec deux arguments, hello et world
# sortie attendue :
hello
world
$ ./args "arg 1" "arg 2" "arg 3" # on lance le programme avec trois arguments
# sortie attendue :
arg 1
arg 2
arg 3
- Vous partirez du fichier C minimal présenté précédemment.
- Vous ferez deux boucles imbriquées (un
while
dans unfor
) pour afficher les caractères un à un. - C'est donc interdit d'utiliser le
%s
deprintf()
. On utilisera%c
.
Exercice 1.2
Voici une structure de liste chainée :
struct chain {
int value;
struct chain *next;
};
// On définit un raccourci pour taper moins de texte :
typedef struct chain chain_t;
On va réimplémenter pas à pas des fonctions permettant de créer et modifier des listes chainées.
Écrivez le tout dans un fichier LOGIN_chain.c
.
Remplacez LOGIN
par votre login. Exemple pour moi : ppompeani_chain.c
.
Étape 1.2.1
Créez une fonction qui permet d'initialiser une liste avec malloc
.
chain_t *chain_new(int value, chain_t *next);
Étape 1.2.2
Créez une fonction qui insère une valeur au début d'une liste, et retourne la nouvelle liste créée.
chain_t *chain_insert(chain_t *list, int value);
- L'argument
list
peut êtreNULL
. - Cette fonction réutilisera
chain_new()
.
Étape 1.2.3
Créez une fonction qui ajoute une valeur à la fin d'une liste, et retourne la nouvelle liste créée.
chain_t *chain_append(chain_t *list, int value);
- L'argument
list
peut êtreNULL
. - Cette fonction réutilisera
chain_new()
. - Cette fonction sera récursive.
Étape 1.2.4
Créez une fonction qui retourne le premier élément de la liste qui vaut la valeur passée en paramètre.
chain_t *chain_find(chain_t *list, int value);
- L'argument
list
peut êtreNULL
. - Cette fonction sera récursive.
- Cette fonction retournera
NULL
si la valeur n'est pas trouvée.
Étape 1.2.5
Créez une fonction qui supprime une liste et tous ses enfants.
void chain_free(chain_t *list);
- L'argument
list
peut êtreNULL
. - Cette fonction sera récursive.
Étape 1.2.6
Créez une fonction qui copie une liste et tous ses enfants.
chain_t *chain_copy(chain_t *list);
- L'argument
list
peut êtreNULL
, auquel cas la fonction renvoieNULL
. - Cette fonction réutilisera
chain_new()
. - Cette fonction sera récursive.
Étape 1.2.7
Créez une fonction qui affiche une liste sur la sortie standard.
void chain_print(chain_t *list);
- L'argument
list
peut êtreNULL
, auquel cas la fonction n'affiche rien. - Cette fonction sera récursive.
- Exemple de sortie :
1, 2, 3, 4\n
.
Étape 1.2.8
Soit la fonction main
suivante :
int main(int argc, char **argv) {
chain_t *my_list = chain_new(1, chain_new(2, chain_new(3, chain_new(4, NULL))));
chain_print(my_list);
chain_t *new_list = chain_copy(my_list);
chain_free(my_list);
chain_print(new_list);
chain_t *list_from_3 = chain_find(new_list, 3);
chain_print(list_from_3);
list_from_3 = chain_append(list_from_3, 5);
chain_print(new_list);
return EXIT_SUCCESS;
}
Exécutez cette fonction avec votre code.
Vérifiez qu'elle affiche cette sortie :
1, 2, 3, 4
1, 2, 3, 4
3, 4
1, 2, 3, 4, 5
Si ce n'est pas le cas, débuggez 🙃
Si c'est le cas, écrivez en commentaire à la fin de votre programme pourquoi le dernier chain_print(new_list)
affiche 1, 2, 3, 4, 5
et pas 1, 2, 3, 4
.
Fin de la partie 1. Votre fichier LOGIN_chain.c
est à rendre sur Moodle.
S'il n'y a pas de zone de rendu sur Moodle, votre fichier est à rendre ici.
J'attends un fichier dont la ou les fonctions main
sont commentées.
Je rajouterai moi-même de façon automatisée une fonction main
ressemblant à celle donnée ci-dessus.
Partie 2 - Fichiers
Le but de cet exercice est d'écrire un programme semblable à la commande grep
.
Là ce n'est que l'introduction. Lisez tout avant de commencer à coder.
Ce programme prend deux paramètres :
- Une chaine de caractères à chercher
- Un chemin de fichier, dans lequel chercher la chaine.
Exemple de fichier :
bonjour.txt
bonjour bonjour
coucou la compagnie
salut tout le monde
salut coucou
hello hello
Exécution :
$ ./grep coucou ./bonjour.txt
coucou la compagnie
salut coucou
Vous écrirez un fichier LOGIN_grep.c
, que vous rendrez au même endroit que le premier exercice.
Étape 2.1
Vérifiez que deux arguments ont bien été donnés au programme.
Si ce n'est pas le cas, affichez un message d'erreur et quittez avec le code d'erreur EXIT_FAILURE
.
Étape 2.2
Lisez le manuel de la fonction fopen()
: man 3 open
.
Ouvrez le fichier passé en paramètre avec la fonction fopen
.
Le fichier doit être ouvert en lecture seule, donnez le flag
approprié.
Seuls 2 arguments sont nécessaires.
S'il y a une erreur, affichez un message d'erreur et quittez avec le code d'erreur EXIT_FAILURE
.
Étape 2.3
Écrivez une fonction qui lit et retourne une ligne d'un fichier passé en paramètre :
char *readline(FILE *file);
- Cette fonction commence par créer un
char *buffer
, et lui allouer de la mémoire via un premiermalloc
- Elle utilise ensuite la fonction
fgetc()
(man 3 fgetc
), pour lire un caractère. - Tant que ce caractère n'est pas
EOF
ou\n
, le buffer est agrandi avecrealloc
(man 3 realloc
) et le caractère est ajouté au buffer. - À la fin,
\0
est ajouté au buffer, pour former une string C valide.
Étape 2.4
Écrire une fonction qui lit tout le fichier passé en paramètre et affiche les lignes qui contiennent la chaine de caractère passée en paramètre :
void grep(FILE *file, char *needle);
Cette fonction utilisera :
readline
écrite à l'étape précédentestrstr
(man 3 strstr
)
Étape 2.5
Appelez grep()
dans votre main et faites quelques tests.
Cette fois-ci, j'attends bien entendu un fichier qui contient une fonction main.
Vous déposerez le fichier.