Licence d’informatique
Module de Pratique du C
Travaux dirigés

TD de pratique du C, manipulation de références et pointeurs

Philippe Marquet

Novembre 2004

Ce document est disponible sous forme d’un fichier PDF.

Les exercices proposés illustrent l’utilisation de références et de pointeurs. La représentation des tableaux à plusieurs dimensions est détaillée. On introduit les différentes classes d’allocation mémoire, dont l’allocation dynamique de mémoire. On traite aussi de la définition de types et de la manipulation de structures autoréférentes.


Exercice 1
 (Passage de paramètres)   Écrivez une fonction qui échange les valeurs de deux variables entières.

Exercice 2
 (Copie de tableau/chaîne de caractères, allocation dynamique de mémoire)  
Question 1   Donnez le code C de la fonction, non normalisée ANSI, de la bibliothèque string
char *strdup(const char *str);
qui retourne une copie fraîchement allouée de la chaîne de caractère str.

On fera appel à la fonction de bibliothèque

#include <stdlib.h>
void *malloc(size_t size);

qui renvoie une référence sur une nouvelle zone mémoire de longueur de size octets. On pourra aussi utiliser les autres fonctions de la bibliothèque string.

Que retourne cette fonction strdup() en cas de problème ?

Question 2   Écrivez une fonction qui renvoie une copie « fraîche » d’un tableau de n entiers passé en paramètre.

Exercice 3
 (Noms temporaires, allocations automatique, statique ou dynamique)   Donnez la définition d’une fonction qui retourne à chaque invocation une chaîne de caractères différente, par exemple en vue de l’utiliser comme un nom de fichier temporaire.

Exercice 4
 (Tableaux à plusieurs dimensions)   Soit la déclaration d’un tableau
int b[3][5];
En considérant que l’allocation du tableau se fait linéairement en mémoire (les 3 « tranches » de b sont allouées à des adresses contiguës), donnez l’état du tableau b après l’exécution du code C suivant :
int b[3][5];
int *a = *b, i;
for (i=0 ; i<15 ; *a++ = i++) 
    ;
**b = 15;           **(b+1) = 16;        *(b[0]+1) = 17;   
*(*b+8) = 18;       *(b[1]+2) = 19;      *(*(b+1)+5) = 20;  
*(b[2]+3) = 21;     *(*(b+2)+2) = 22; 

Exercice 5
 (Manipulation de polynômes)   Un polynôme est une liste de monômes. Un monôme est caractérisé par un coefficient (considéré entier) et un degré. On ne rangera en mémoire que les monômes de coefficient différent de zéro.

Par exemple le polynôme x7+5x3−4x+2 constitué de 4 monômes sera représenté par :

Question 1   Donnez des définitions des types de données monome_t et polynome_t.
Question 2   Écrivez une fonction qui incrémente le degré de tous les monômes d’un polynôme.
Question 3   Écrivez une fonction qui évalue un polynôme pour une valeur passée en paramètre.
Question 4   Écrivez une fonction qui imprime sur la sortie standard la représentation d’un polynôme.
Question 5   Écrivez une fonction qui libère la mémoire occupée par un polynôme.
Question 6   Écrivez une fonction qui lit sur l’entrée standard un polynôme donné sous la forme d’une suite de coefficients des monômes de degrés décroissants. Le premier entier entré représente le coefficient du monôme de degré le plus élevé ; le nombre de coefficients entrés est égal au degré du polynôme plus 1. Par exemple, le polynôme de l’exemple est entré par la suite :
1 0 0 0 5 0 -4 2            
Question 7   Écrivez une fonction qui additionne un monôme à un polynôme. Attention, on ne stocke jamais les monômes dont le coefficient est nul.
Question 8   Soyez rassurés, on ne vous demande pas de donner la définition d’une fonction qui additionne (ou multiplie !) deux polynômes. Donnez simplement les prototypes de telles fonctions.

Exercice 6
 (Gestion de piles)   Il s’agit de donner une implantation des fonctions suivantes de manipulation d’une pile d’entiers
int is_empty(pile_t p);
int push(pile_t *pp, int val);
int pop(pile_t *pp, int *val);
Chacune de ces fonctions retourne une valeur non nulle en cas d’erreur.

La spécification précise que la taille de la pile n’est pas bornée, aussi utilisera-t-on une allocation dynamique de mémoire.

Question 1  
Question 2   Donnez les définitions des fonctions is_empty(), push(), et pop().
Question 3   Identifiez les fonctions supplémentaires que requière l’utilisation de valeurs de type pile_t. Donnez le fichier d’entête d’une bibliothèque de manipulation de piles.
Question 4   Soit la fonction next_token() suivante
/* next_token() retourne un token et positionne val en cas de token INT_TK */
enum token_e {INT_TK,           /* un entier */
              PRINT_TK,         /* affichage */
              ADD_TK,           /* addition  */
              MUL_TK,           /* produit */
              EOF_TK,           /* EOF */
              NONE_TK};         /* erreur */
static int val;
enum token_e next_token(void);
Écrivez un programme pour une simple calculette postfixée munie des opérations d’addition et de multiplication.

Exercice 7
 (Tableaux grandissants)   Il s’agit de gérer des tableaux, définis comme une structure de données avec des accès indexés à partir de zéro aux données, dont la taille augmente avec les accès aux éléments grandissants.

L’interface de cette structure de données est, par exemple pour des grands tableaux d’entiers :

int ga_set(struct ga_s *ga, unsigned int index, int val);
int ga_get(struct ga_s *ga, unsigned int index, int *val);
struct ga_s ga_init(void); 
int ga_destroy(struct ga_s *ga); 
Question 1   Donnez la définition de l’implantation d’une telle structure par un tableau alloué dynamiquement dont on conserve la taille.

Quels sont les avantages et inconvénients d’une telle implantation ?

Question 2   Les inconvénients de la première implantation peuvent être supprimés en multipliant les zones mémoires nécessaires au fur et à mesure de l’agrandissement de la structure. Il s’agit alors de définir une structure de données pour regrouper ces zones mémoires. Donnez la définition d’une nouvelle implantation basée sur ce principe.

Exercice 8
 (Allocation via un pool)   Plutôt que d’allouer dynamiquement au fur et à mesure de nombreuses petites structures, on peut gérer, au niveau applicatif, un pool de structures. Les requêtes d’allocation sont alors satisfaites en retournant des structures de ce pool, les désallocations venant à nouveau nourrir le pool, une allocation dynamique effective n’ayant lieu que lorsque le pool est vide.

Proposez une interface et son implantation pour la gestion d’un pool de valeurs d’un type struct_t connu.


Ce document a été traduit de LATEX par HEVEA