Programmation C
Modularité - Exercices
Cours Exercices Correction des exercices

Exercice 1 Exercice 2 Exercice 3 Exercice 4
Exercice 5 Exercice 6 Exercice 7 Exercice 8

Exercice 1

  • On donne le fichier code source complet suivant :

#include <stdio.h>

long long factoriel(int n) {
  if (n == 0) {
    return 1;
  }
  else {
    return n * factoriel(n - 1);
  }
}

int main(void) {
  for (int i = 0; i < 20; i++) {
    printf("%2d! = %18lld\n", i, factoriel(i));
  }
  return 0;
}

09-Exercice1.c - 09-Exercice1.exe
 0! =                  1
 1! =                  1
 2! =                  2
 3! =                  6
 4! =                 24
 5! =                120
 6! =                720
 7! =               5040
 8! =              40320
 9! =             362880
10! =            3628800
11! =           39916800
12! =          479001600
13! =         6227020800
14! =        87178291200
15! =      1307674368000
16! =     20922789888000
17! =    355687428096000
18! =   6402373705728000
19! = 121645100408832000
  • Découper ce fichier en un module .c utilitaire et son fichier .h associé contenant la fonction factoriel et un fichier .c ne contenant que la fonction principale.

Exercice 2

  • On donne le fichier code source complet suivant :

#include <stdio.h>
#include <math.h>

typedef struct {
  double a;
  double b;
  double c;
equationSecondDegre;

double calculerY(equationSecondDegre e, double x) {
  return e.a * x * x + e.b * x + e.c;
}

void afficherEquationSecondDegre(equationSecondDegre e) {
  printf("f(x) = %lf x2 + %lf x + %lf", e.a, e.b, e.c);
}

double calculerDelta(equationSecondDegre e) {
  return e.b * e.b - 4.0 * e.a * e.c;
}

int calculerRacines(equationSecondDegre e,double *r1,double *r2) {
  double delta = calculerDelta(e);
  if (delta < 0.0) {
    return 0;
  }
  if (delta < 0.0) {
    *r1 = -e.b / (2.0 * e.a);
    return 1;
  }
  double sqrtDelta = sqrt(delta);
  *r1 = (-e.b - sqrtDelta) / (2.0 * e.a);
  *r2 = (-e.b + sqrtDelta) / (2.0 * e.a);
  return 2;
}

int main(void) {
  equationSecondDegre eq = { 1.2,-2.3,-6.4 };
  afficherEquationSecondDegre(eq);
  printf("\n");
  printf("\n");
  printf("Pour x = %lf, f(x) = %lf\n", 2.3, calculerY(eq, 2.3));
  printf("\n");
  double r1;
  double r2;
  int nbRacines = calculerRacines(eq, &r1, &r2);
  switch (nbRacines) {
    case 0: {
      printf("La fonction f n'a pas de racine reelle\n");
      break;
    }
    case 1: {
      printf("La fonction f a une racine reelle : %lf\n", r1);
      printf("Pour x = %lf, f(x) = %lf\n", r1, calculerY(eq, r1));
      break;
    }
    case 2: {
      printf("La fonction f a deux racines reelles : %lf et %lf\n", r1, r2);
      printf("Pour x = %lf, f(x) = %lf\n", r1, calculerY(eq, r1));
      printf("Pour x = %lf, f(x) = %lf\n", r2, calculerY(eq, r2));
      break;
    }
  }
  return 0;
}

09-Exercice2.c - 09-Exercice2.exe
f(x) = 1.200000 x2 + -2.300000 x + 6.400000
Pour x = 2.300000, f(x) = 7.458000
  • Découper ce fichier en un module .c dédié à la struct equationSecondDegre, aux fonctions qui l'utilisent et aux fonctions utilisées par les fonctions qui l'utilisent.

Exercice 3

  • On donne le fichier code source complet suivant :

#include <stdio.h>
#include <math.h>

typedef struct {
  double x;
  double y;
  double z;
sommet;

typedef struct {
  sommet s1;
  sommet s2;
  sommet s3;
triangle;

double calculerDistance(sommet s1, sommet s2) {
  return sqrt(pow(s2.x - s1.x, 2.0) +
              pow(s2.y - s1.y, 2.0) +
              pow(s2.z - s1.z, 2.0));
}

double calculerPerimetre(triangle t) {
  return calculerDistance(t.s1, t.s2) +
    calculerDistance(t.s2, t.s3) +
    calculerDistance(t.s3, t.s1);
}

double calculerAire(triangle t) {
  double s = calculerPerimetre(t) / 2.0;
  double a = calculerDistance(t.s1, t.s2);
  double b = calculerDistance(t.s2, t.s3);
  double c = calculerDistance(t.s3, t.s1);
  return sqrt(s*(s-a)*(s-b)*(s-c));
}

void afficherSommet(sommet s) {
  printf("(");
  printf("%.3lf",s.x);
  printf(",");
  printf("%.3lf", s.y);
  printf(",");
  printf("%.3lf", s.z);
  printf(")");
}

void afficherTriangle(triangle t) {
  printf("(");
  afficherSommet(t.s1);
  printf(",");
  afficherSommet(t.s2);
  printf(",");
  afficherSommet(t.s3);
  printf(")");
}

int main(void) {
  triangle t = { { -2.2,-0.3,6.4 },
                 {  3.1,-2.3,4.1 },
                 {  3.6, 2.7,0.4 } };
  //triangle t = { {  4.0, 0.0,1.4 },
  //               {  0.0, 0.0,1.4 },
  //               {  0.0, 3.0,1.4 } };
  afficherTriangle(t);
  printf("\n");
  printf("Perimetre = %lf\n", calculerPerimetre(t));
  printf("Aire      = %lf\n", calculerAire(t));
  printf("\n");
  return 0;
}

09-Exercice3.c - 09-Exercice3.exe
((-2.200,-0.300,6.400),(3.100,-2.300,4.100),(3.600,2.700,0.400))
Perimetre = 21.222031
Aire      = 19.067194
  • Découper ce fichier en modules .c et .h utilisés par le programme principal.

Exercice 4

  • Une sphère 3D est définie par la donnée
    • de la valeur réelle son rayon
    • de la position 3D de son centre
  • Développer les fichiers .h pouvant être utilisés pour définir une variable de type sphere 3D.
    Tester ces fichiers (ces ébauches de modules) au moyen d'un programme principal.
  • On souhaite tester si deux sphères ont une intersection (si la distance qui les sépare est inférieure ou égale à la somme de leurs rayons respectifs).
    Modifier les fichiers pour ajouter une fonction de test.
    Valider cette fonction via le programme principal.

Exercice 5

  • On donne la struct suivante qui permet de coder des couleurs RVB avec 8 bits de rouge, de vert et de bleu.
    typedef struct {
      unsigned char r;
      unsigned char v;
      unsigned char b; };
  • Développer un module permettant de définir huit constantes pour les couleurs
    • noir
    • rouge
    • vert
    • bleu
    • cyan
    • magenta
    • jaune
    • blanc

Exercice 6

  • Aux échecs chaque joueur dispose de 16 pièces qui se répartissent de la façon suivante :
    • un roi
    • une reine
    • deux fous
    • deux tours
    • deux cavaliers
    • huit pions
  • Il y a donc 6 types de pièces
  • Développer un module sous la forme d'un fichier .h permettant de déclarer un type enum codePiece de codage des types de pièces au moyen d'entiers.

Exercice 7

  • On souhaite poser les bases d'un travail d'informatisation de la gestion des feuilles de matches pour une ligue de football. Ce travail préliminaire porte sur le stockage des informations relatives à un match suivantes :
    • Les noms des deux équipes
    • La date du match
    • Le résultat du match
  • Développer une décomposition en modules du stockage de l'information relative à un match. Autant que possible le stockage de l'information devra utiliser une gestion dynamique de la mémoire.
  • On souhaite maintenant ajouter les compositions des deux équipes en autorisant le stockage d'un nombre arbitraire de joueurs pour chacune des deux équipes. Un joueur est caractérisé par un numéro de maillot, un nom et un numéro de licence (chaîne de caractères). Modifier le résultat obtenu à la question précédente pour inclure cette possibilité.
  • On souhaite faire une nouvelle modification consistant à stocker les buts marqués. Un but est caractérisé par la minute de jeu lors de laquelle il a été marqué, par le numéro de maillot du joueur qui l'a marqué, par l'équipe à laquelle appartient le joueur qui l'a marqué et par le fait qu'il s'agisse d'un but contre son camp ou non. Modifier une nouvelle fois le travail réalisé à l'issue des deux premières questions.

Exercice 8

  • Une file (une file d'attente) est une structure informatique de stockage d'informations. Une file est munie d'une entrée et d'une sortie. Les informations entrent les unes après les autres par l'entrée, sortent les une après les autres par la sortie et ne se doublent pas entre l'entrée et la sortie (quelles que soient les informations A et B, si A entre avant B dans une file, alors A sort avant B). Suivant la façon avec laquelle une file est implantée, elle peut contenir des informations de même nature comme des informations de natures différentes.
  • Implanter avec opacification une struct file de double en la munissant d'une fonction pour chacune des opérations suivantes :
    • Création d'une file (avec test de bonne création)
    • Destruction d'une file
    • Entrée d'un double (avec test de bonne entrée)
    • Sortie d'un double (avec test de bonne sortie)
    • Test du fait qu'une file est vide
  • La façon avec laquelle le stockage des informations est réalisé est a priori libre. Il existe beaucoup de façon de faire répondant à des contraintes ou des finalités variées.
    • Dynamicité de l'utilisation de la mémoire
    • Rapidité d'utilisation
    • Limitation de l'empreinte mémoire
    • ...
  • Toutes peuvent être implantées de façon que les signatures des fonctions soient les mêmes (effet boite noire). Quels que soient les choix d'implantation, ce ne sont que les fonctions qui sont utilisées pour les utiliser.
  • Implantations possibles du stockage au sein d'une struct pileDeDouble
    • Tableau statique (ou dynamique de taille fixe) avec dernière valeur entrée à l'indice 0 et stockage de l'indice de prochaine valeur à sortir
      • Taille de la file limitée à la taille du tableau
      • Obligation de réaliser un décalage d'une cellule de l'ensemble des valeurs à chaque nouvelle entrée
    • Tableau statique (ou dynamique de taille fixe) géré de façon cyclique avec stockage de l'indice de la prochaine valeur à sortir et stockage du nombre de valeurs stockées
      • Taille de la file limitée à la taille du tableau
      • Déplacement des deux indices au cours des entrées et sorties
      • Interdiction que l'indice d'entrée rattrape l'indice de sortie
      • Pas de décalage -> rapidité
    • Tableau dynamique avec dernière valeur entrée à l'indice 0 et stockage de l'indice de prochaine valeur à sortir
      • Pas de limite à la taille de la file
      • Modification de la taille du tableau gourmande en temps donc stockage probablement peu efficace si ajustement lors de toute entrée et toute sortie
    • Liste dynamique doublement chainée (pointeur sur successeur et pointeur sur prédécesseur)
      • Pas de limite à la taille de la file à part la taille du tas
      • Pas forcément économique en mémoire car stockage de deux pointeurs pour chaque valeur double présente dans une file
      • Beaucoup d'allocations et de désallocations de mémoire -> stress possible du système d'exploitation et gâchis de mémoire
    • Variantes des solutions dynamiques précédentes visant à optimiser les allocations de mémoire
  • Valider le bon fonctionnement des cinq fonctions