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 un int,
  • %c pour un char,
  • %s pour un char *,

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

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);

É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);

É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);

Étape 1.2.5

Créez une fonction qui supprime une liste et tous ses enfants.

void chain_free(chain_t *list);

Étape 1.2.6

Créez une fonction qui copie une liste et tous ses enfants.

chain_t *chain_copy(chain_t *list);

Étape 1.2.7

Créez une fonction qui affiche une liste sur la sortie standard.

void chain_print(chain_t *list);

É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 :

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);

É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 :

É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.