Langage C - TP 5: Allocation mémoire, Définition de type & Structures

1. Calcul de statistiques

Cette section illustre la gestion de la mémoire dynamique en langage C par la mise en place d'un programme de calcul statistique. Le travail vise à réaliser des calculs de statistique simples (moyenne, variance, médiane) à partir de séries de valeurs de taille dynamique. Les pointeurs et l’allocation dynamique sont une bonne solution pour implanter ce genre de traitement.

Exercice 1.1.

Ecrire un programme tp5-stat.c contenant une fonction int* creation_serie(unsigned int n) qui crée dynamiquement un espace en mémoire permettant de stocker une série de valeurs de taille n et retourne un pointeur vers celui-ci. Le programme doit contenir également une fonction main qui permet de faire saisir à l’utilisateur une série de 5 valeurs.

Exemple d’exécution

Valeur 1/5: 2
Valeur 2/5: 3
Valeur 3/5: 4
Valeur 4/5: 5
Valeur 5/5: 6

Exercice 1.2.

Dans le programme tp5-stat.c, ajouter une fonction void affiche_serie(int* s, unsigned int n) qui affiche la série de valeurs pointée par la variable s sous la forme : ( s[0], …, s[i], …, s[n-1] ). Modifier la fonction main du programme pour afficher la série des 5 valeurs saisies.

Exemple d’exécution

Valeur 1/5: 2
Valeur 2/5: 3
Valeur 3/5: 4
Valeur 4/5: 5
Valeur 5/5: 6

Serie: ( 2, 3, 4, 5, 6 ) 

Exercice 1.3.

Dans le programme tp5-stat.c, ajouter une fonction void destruction_serie(int** ps) qui désalloue l’espace mémoire contenant la série pointée par la variable ps et affecte la valeur NULL à la série pointée. Modifier la fonction main du programme pour détruire la série utilisée avant la fin de l’exécution.

Exemple d’exécution

Valeur 1/5: 2
Valeur 2/5: 3
Valeur 3/5: 4
Valeur 4/5: 5
Valeur 5/5: 6

Serie: ( 2, 3, 4, 5, 6 ) 

Moyenne empirique

Soit $X=(x_{1},\ldots ,x_{i},\ldots ,x_{n})$ une série de $n$ valeurs. La moyenne empirique de $X$, notée $\overline{X}$, est définie par la somme des valeurs de $X$ divisée par le nombre $n$ de valeur. Plus formellement:

$$\overline{X}\ =\ \frac{1}{n}\sum_{i=1}^{n}x_{i}$$ 

Exercice 1.4.

Dans le programme tp5-stat.c, ajouter une fonction float moyenne(int* s, unsigned int n) qui calcule et retourne la moyenne de la série de $n$ valeurs pointée par s. Si la série est vide, la fonction doit retourner la valeur spéciale NAN (Not a Number) de l’en-tête math.h. Modifier la fonction main du programme pour afficher la série des 5 valeurs saisies et sa moyenne.

Exemple d’exécution

Valeur 1/5: 6
Valeur 2/5: 7
Valeur 3/5: 2
Valeur 4/5: 4
Valeur 5/5: 9

La moyenne de ( 6, 7, 2, 4, 9 ) est 5.600000

Variance

Soit $X=(x_{1},\ldots ,x_{i},\ldots ,x_{n})$ une série de $n$ valeurs et soit $\overline{X}$ la moyenne empirique de $X$. La variance, notée $V$, de la série $X$ est définie par la moyenne des carrés des écarts entre les valeurs de $X$ et la moyenne empirique $\overline{X}$. Plus formellement :

$$V=\frac{1}{n}\sum_{i=1}^{n}(x_{i}-\overline{X})^{2}$$ 

Exercice 1.5.

Dans le programme tp5-stat.c, ajouter une fonction float variance(int* s, unsigned int n) qui calcule et retourne la variance de la série de $n$ valeurs pointée par s. Si la série est vide, la fonction doit retourner la valeur spéciale NAN (Not a Number) de l’en-tête math.h. Modifier la fonction main d'afficher la variance de la série saisie.

Exemple d’exécution

Valeur 1/5: 9
Valeur 2/5: 8
Valeur 3/5: 4
Valeur 4/5: 1
Valeur 5/5: 2

La moyenne de ( 9, 8, 4, 1, 2 ) est 4.800000

Sa variance est de 10.1600000

Exercice 1.6.

Dans le programme tp5-stat.c ajouter une fonction int* tri_croissant(int* s, unsigned int n) qui retourne un pointeur vers une série contenant les n valeurs pointée par s triées dans l’ordre croissant. Si la série s est vide, la fonction doit retourner NULL. La série s ne doit pas être modifiée par la fonction et le choix de l’algorithme de tri est libre. Modifier la fonction main afin d’y ajouter l’affichage de la série triée (penser à bien désallouer tous les pointeurs avant la fin de l’exécution).

Aide: Pour trier une série Il est possible d'adapter l'un des algorithmes développés dans le TP sur les tableaux.

Exemple d’exécution

Valeur 1/5: 5
Valeur 2/5: 1
Valeur 3/5: 2
Valeur 4/5: 4
Valeur 5/5: 3
Serie originale: ( 5, 1, 2, 4, 3 )
Serie triee: ( 1, 2, 3, 4, 5 ) 

Médiane

Soit $X=(x_{1},\ldots ,x_{i},\ldots ,x_{n})$ une série de $n$ valeurs triées par ordre croissant. La médiane, notée $Me$, de la série $X$ est définie par la valeur située au milieu de la séquence. Plus formellement :
$$
Me\ =\ \left\{
\begin{array}{l}
  x_{\frac{n}{2}},\ \textrm{si n est pair }\\
  x_{\frac{n+1}{2}},\ \textrm{si n est impair }
\end{array}
\right.
$$

Exercice 1.7.

Dans le programme tp5-stat.c ajouter une fonction float mediane(int* s, unsigned int n) qui calcule et retourne la médiane de la série de n valeurs pointée par s. Si la série est vide, la fonction doit retourner la valeur spéciale NAN (Not a Number) de l’en-tête math.h. Modifier la fonction main afin d’y ajouter l’affichage de la médiane de la série saisie.

Exemple d’exécution

Valeur 1/5: 5
Valeur 2/5: 1
Valeur 3/5: 2
Valeur 4/5: 4
Valeur 5/5: 6
La valeur mediane de ( 5, 1, 2, 4, 6 ) est 4.000000

2. Structures

Il est apparu dans la partie 1 de ce travail qu’afin de gérer une série de valeur il est indispensable de connaitre :

  • Le pointeur vers la série
  • Le nombre de valeurs contenues dans la série

Une telle configuration alourdi les prototypes de fonctions utilisant des séries et peut entrainer des erreurs si l’une des deux informations vient à être modifiée.

Afin de résoudre ce problème, une solution est de grouper les deux informations au sein d’une même structure.

Exercice 2.1.

Ecrire un programme tp5-serie.c contenant la déclaration d’un type permettant de représenter une série de valeurs. Ce type sera nommé serie et sera définit par une structure contenant les champs suivants :

  • Une variable nommé valeurs de type int*.
  • Une variable taille de type unsigned int représentant le nombre de valeurs de la série.

Le programme doit contenir également une fonction main qui déclare une variable s de type serie.

Exercice 2.2.

En s'inspirant de l’exercice 1.1. de la partie 1, dans le programme tp5-serie.c, ajouter une fonction serie creation_serie(unsigned int n) qui crée dynamiquement une série pouvant contenir n valeurs. Modifier la fonction main afin de faire saisir à l’utilisateur la taille de la série qu'il souhaite créer puis les valeurs de celle-ci .

Exemple d’exécution

Nombre de valeurs: 5
Valeur 1/5: 1
Valeur 2/5: 2
Valeur 3/5: 3
Valeur 4/5: 4
Valeur 5/5: 5

Exercice 2.3.

En s'inspirant de l’exercice 1.2. de la partie 1, dans le programme tp5-serie.c, ajouter une fonction void affiche_serie(serie s) qui affiche la série s sous la forme :
( s[0], …, s[i], …, s[n-1] ). Modifier la fonction main du programme pour afficher la série de valeurs saisie.

Exemple d’exécution

Nombre de valeurs: 6
Valeur 1/6: 1
Valeur 2/6: 4
Valeur 3/6: 3
Valeur 4/6: 2
Valeur 5/6: 5
Valeur 6/6: 6
Serie: ( 1, 4, 3, 2, 5, 6 )

Pointeurs vers des structures

Soit une structure ma_structure contenant un champ c (par exemple de type int). Pour une variable v dont le type est ma_structure, l'accès à la valeur du champ c de v se fait en utilisant le point:

typedef struct {
    int c;
} ma_structure;

int main(){

    ma_structure v;

    v.c = 1;
}

Soit la variable pv déclarée comme un pointeur vers une variable de type ma_structure:

typedef struct {
    int c;
} ma_structure;

int main(){

    ma_structure v;

    ma_structure* pv = &v;
}

L'accès à la valeur du champ c de v peut se faire de 2 façons équivalentes:

  • (*pv).c (ecriture classique du déréférencemet)
  • pv->c (écriture concise via l'opérateur ->)

ces deux écritures produisent le même comportement:

typedef struct {
    int c;
} ma_structure;

int main(){

    ma_structure v;

    ma_structure* pv = &v;

    (*pv).c = 1;

    pv->c = 1;
}

Exercice 2.4.

En s'inspirant de l’exercice 1.3. de la partie 1, dans le programme tp5-serie.c, ajouter une fonction void destruction_serie(serie* ps) qui désalloue l’espace mémoire contenant la série pointée par la variable ps et affecte la valeur NULL à la variable valeurs de la série et 0 à sa taille. Modifier la fonction main du programme pour détruire la série utilisée avant la fin de l’exécution et afficher l'adresse de ses valeurs et la valeur de sa taille.

Exemple d’exécution

Nombre de valeurs: 6
Valeur 1/6: 1
Valeur 2/6: 4
Valeur 3/6: 6
Valeur 4/6: 3
Valeur 5/6: 2
Valeur 6/6: 5
Serie: ( 1, 4, 6, 3, 2, 5 )
Serie desallouee: valeurs = (nil), taille = 0

Exercice 2.5.

En s'inspirant de l’exercice 1.4. de la partie 1, dans le programme tp5-serie.c, ajouter une fonction float moyenne(serie s) qui calcule et retourne la moyenne de la série s. Si la série est vide, la fonction doit retourner la valeur spéciale NAN (Not a Number) de l’en-tête math.h. Modifier la fonction main afin d'afficher la moyenne de la série saisie.

Exemple d’exécution

Nombre de valeurs: 6
Valeur 1/6: 1
Valeur 2/6: 3
Valeur 3/6: 2
Valeur 4/6: 5
Valeur 5/6: 4
Valeur 6/6: 6

Serie: ( 1, 3, 2, 5, 4, 6 )

Moyenne de la serie: 3.500000

Serie desallouee
  Valeurs: (nil)  taille: 0

Exercice 2.6.

En s'inspirant de l’exercice 1.5. de la partie 1, dans le programme tp5-serie.c, ajouter une fonction float variance(serie s) qui calcule et retourne la variance de la série s. Si la série est vide, la fonction doit retourner la valeur spéciale NAN (Not a Number) de l’en-tête math.h. Modifier la fonction main afin d’y ajouter l’affichage de la variance de la série saisie.

Exemple d’exécution

Nombre de valeurs: 7
Valeur 1/7: 1
Valeur 2/7: 1
Valeur 3/7: 3
Valeur 4/7: 9
Valeur 5/7: 4
Valeur 6/7: 8
Valeur 7/7: 1
Serie: ( 1, 1, 3, 9, 4, 8, 1 )

Moyenne de la serie: 3.857143

Variance de la serie: 9.836734

Serie desallouee
  Valeurs: (nil)  taille: 0

Exercice 2.7.

En s'inspirant de l’exercice 1.6. de la partie 1, dans le programme tp5-serie.c, ajouter une fonction serie tri_croissant(serie s) qui retourne une série contenant mêmes valeurs que celles contenues dans la série s mais triées dans l’ordre croissant. La série s ne doit pas être modifiée. Si la série s est vide, la fonction doit retourner une série vide (valeurs à NULL et taille à 0). Modifier la fonction main afin d’y ajouter l’affichage de la série triée (penser à bien désallouer tous les pointeurs avant la fin de l’exécution).

Exemple d’exécution

Nombre de valeurs: 6
Valeur 1/6: 1
Valeur 2/6: 3
Valeur 3/6: 6
Valeur 4/6: 4
Valeur 5/6: 2
Valeur 6/6: 5
Serie: ( 1, 3, 6, 4, 2, 5 )

Moyenne de la serie: 3.500000

Variance de la serie: 2.916667

Serie triee: ( 1, 2, 3, 4, 5, 6 )

Serie desallouee
  Valeurs: (nil)  taille: 0

Serie triee desallouee
  Valeurs: (nil)  taille: 0

Exercice 2.8.

En vous inspirant de l’exercice 1.7. de la partie 1, dans le programme tp5-serie.c, ajouter une fonction float mediane(serie s) qui calcule et retourne la médiane de la série s. Si la série est vide, la fonction doit retourner la valeur spéciale NAN (Not a Number) de l’en-tête math.h. Modifier la fonction main afin d’y ajouter l’affichage de la médiane de la série saisie.

Exemple d’exécution

Nombre de valeurs: 6
Valeur 1/7: 11
Valeur 2/7: 2
Valeur 3/7: 1
Valeur 4/7: 6
Valeur 5/7: 4
Valeur 6/7: 1
Valeur 7/7: 3
Serie: ( 1, 2, 1, 6, 4, 1, 3 )

Serie triee: ( 1, 1, 1, 2, 3, 4, 6 )

Moyenne de la serie: 2.571429

Variance de la serie: 3.102041

Mediane de la serie: 2.000000

Serie desallouee
  Valeurs: (nil)  taille: 0

Serie triee desallouee
  Valeurs: (nil)  taille: 0

3. Gestion de classes

Cette section vise à illustrer les capacités de calcul statistique implantées précédemment en simulant la gestion des notes d’un ensemble de classes dans une école. D’un point de vue statistique, une classe est représentée par une série de valeurs correspondant chacune à la moyenne individuelle d’un élève de la classe (les moyennes seront représentées par des nombres entiers entre $0$ et $20$).

Une école est un ensemble de classes chacune identifiée par un nombre entier allant de $1$ (première classe) à $n$ (dernière classe). Chaque classe peut être composée d’un nombre différent d’élèves et chaque école ayant un nombre variable de classes. Par exemple, une école comportant 3 classes, la première composée de 8 élèves, la seconde de 6 élèves et la troisième de 7 élèves peut se représenter comme suit: 

Dans la figure précédente, le premier élève de la classe 1 à une moyenne de 15, le premier élève de la classe 2 à une moyenne de 13, le troisième élève de la classe 3 à une moyenne de 11.

Exercice 3.1.

Ecrire un programme tp5-ecole.c contenant la définition d’un type ecole reposant sur une structure dont les champs sont :

  • Un pointeur nommé classes vers un ensemble de serie (réutiliser le type définit pour l’exercice 2.1 de la partie 2)
  • Une variable nommée nb_classes de type unsigned int qui représente le nombre de classes présentes dans l’école.

Le programme doit contenir également une fonction main qui déclare une variable e de type ecole.

Exercice 3.2.

Dans le programme tp5-ecole.c, ajouter une fonction ecole creation_ecole(unsigned int m) qui crée une ecole de m classes. Si la valeur de m est strictement inférieure à 1, la fonction renvoie une variable de type ecole dont le champ classes est à NULL et le champ nb_classes à 0. Modifier la fonction main afin de permettre à l’utilisateur de saisir un nombre de classes et de créer une variable e de type ecole avec le nombre de classes adéquat. Le programme affichera le nombre de classes crées et l'adresse pointée par la variable e.classes.

Exemple d’exécution

Nombre de classes dans l'ecole: 3

Classes dans l'ecole: 3
Addresse de e.classes: 0x5b2d29bd0ac0

Exercice 3.3.

Dans le programme tp5-ecole.c, ajouter une fonction void saisie_ecole(ecole e) qui demande à l’utilisateur de saisir l’ensemble des notes d’une école. L’école utilisée doit avoir été créée avant d’être saisie. La fonction a pour algorithme :

pour chaque classe c de e
  demander le nombre d’eleves
  créer une serie à partir du nombre d’élèves
  pour chaque eleve e
    demander sa moyenne individuelle
    affecter la moyenne a la valeur e de la serie
  finpour
  affecter la serie saisie à la classe c
finpour

Aide: Il est recommandé de réutiliser toutes les fonctions relatives aux séries implantées lors de la partie 2 de ce travail.

Exemple d’exécution

Nombre de classes dans l'ecole: 3

Nombre d'eleves de la classe 1: 5
  Note de l'eleve 1/5: 12
  Note de l'eleve 2/5: 13
  Note de l'eleve 3/5: 15
  Note de l'eleve 4/5: 14
  Note de l'eleve 5/5: 14
Nombre d'eleves de la classe 2: 4
  Note de l'eleve 1/4: 4
  Note de l'eleve 2/4: 15
  Note de l'eleve 3/4: 16
  Note de l'eleve 4/4: 9
Nombre d'eleves de la classe 3: 6
  Note de l'eleve 1/6: 11
  Note de l'eleve 2/6: 12
  Note de l'eleve 3/6: 11
  Note de l'eleve 4/6: 16
  Note de l'eleve 5/6: 14
  Note de l'eleve 6/6: 9

Classes dans l'ecole: 3
Addresse de e.classes: 0x5d047a6f5ac0

Exercice 3.4.

Dans le programme tp5-ecole.c, ajouter une fonction void affiche_ecole(ecole e) qui affiche l’école e sous la forme :

Classe 1 : ( s[0], …, s[i], …, s[n-1] )

Classe m : ( s[0], …, s[j], …, s[k-1] )

Modifier la fonction main du programme pour y ajouter l’affichage de l’école saisie.

Aide: Il est recommandé de réutiliser toutes les fonctions relatives aux séries implantées lors de la partie 2 de ce travail.

Exemple d’exécution

Nombre de classes dans l'ecole: 3

Nombre d'eleves de la classe 1: 5
  Note de l'eleve 1/5: 12
  Note de l'eleve 2/5: 13
  Note de l'eleve 3/5: 16
  Note de l'eleve 4/5: 14
  Note de l'eleve 5/5: 11
Nombre d'eleves de la classe 2: 4
  Note de l'eleve 1/4: 16
  Note de l'eleve 2/4: 13
  Note de l'eleve 3/4: 12
  Note de l'eleve 4/4: 15
Nombre d'eleves de la classe 3: 6
  Note de l'eleve 1/6: 18
  Note de l'eleve 2/6: 11
  Note de l'eleve 3/6: 12
  Note de l'eleve 4/6: 11
  Note de l'eleve 5/6: 16
  Note de l'eleve 6/6: 15

Classe 1: ( 12, 13, 16, 14, 11 )
Classe 2: ( 16, 13, 12, 15 )
Classe 3: ( 18, 11, 12, 11, 16, 15 )

Classes dans l'ecole: 3
Addresse de e.classes: 0x560fb152dac0

Exercice 3.5.

Dans le programme tp5-ecole.c, ajouter une fonction void destruction_ecole(ecole* pe) qui désalloue toute la mémoire utilisée par l’ecole pointée par pe. Modifier la fonction main du programme pour détruire l’école utilisée avant la fin de l’exécution.

Aide: Il est recommandé de réutiliser toutes les fonctions relatives aux séries implantées lors de la partie 2 de ce travail.

Exemple d’exécution

Nombre de classes dans l'ecole: 3

Nombre d'eleves de la classe 1: 4
  Note de l'eleve 1/4: 15
  Note de l'eleve 2/4: 16
  Note de l'eleve 3/4: 12
  Note de l'eleve 4/4: 11
Nombre d'eleves de la classe 2: 3
  Note de l'eleve 1/3: 14
  Note de l'eleve 2/3: 16
  Note de l'eleve 3/3: 12
Nombre d'eleves de la classe 3: 4
  Note de l'eleve 1/4: 16
  Note de l'eleve 2/4: 15
  Note de l'eleve 3/4: 12
  Note de l'eleve 4/4: 9

Classe 1: ( 15, 16, 12, 11 )
Classe 2: ( 14, 16, 12 )
Classe 3: ( 16, 15, 12, 9 )

Destruction de e

Classes dans l'ecole: 0
Addresse de e.classes: (nil)

Exercice 3.6.

Dans le programme tp5-ecole.c, Modifier la fonction main du programme pour permettre à l’utilisateur :

  • De saisir une école complète
  • D’afficher la moyenne d’une classe
  • D’afficher la variance d’une classe
  • D’afficher la médiane d’une classe
  • De quitter le programme

Aide: Il est recommandé de réutiliser toutes les fonctions relatives aux séries implantées lors de la partie 2 de ce travail.

Un exemple de fonction main à compléter pour gérer le menu:

void main(void){

  ecole e;
  
  unsigned int n;
  
  char choix = ' ';
  
  while(choix != 'q'){
  
    printf("\nGestion d'ecole\n");
    printf("1: Saisir une ecole\n");
    printf("2: Moyenne d'une classe\n");
    printf("3: Variance d'une classe\n");
    printf("4: Mediane d'une classe\n");
    printf("q: Quitter\n\n");
	
	printf("Choix: ");

	scanf("%c", &choix);
	getchar();
  
    switch(choix){
        case '1': // a completer
                  break;

        case '2': // a completer
                  break;

        case '3': // a completer
                  break;

        case '4': // a completer
                  break;

		case 'q': break;

        default: printf("Choisir 1, 2, 3, 4 ou q.");
	}
  }
  

  destruction_ecole(&e);
  
  printf("\n\nClasses dans l'ecole: %u", e.nb_classes);
  printf("\nAddresse de e.classes: %p", e.classes);
  
}

Exemple d’exécution

Nombre de classes dans l'ecole: 3

Nombre d'eleves de la classe 1: 4
  Note de l'eleve 1/4: 15
  Note de l'eleve 2/4: 16
  Note de l'eleve 3/4: 12
  Note de l'eleve 4/4: 11
Nombre d'eleves de la classe 2: 3
  Note de l'eleve 1/3: 14
  Note de l'eleve 2/3: 16
  Note de l'eleve 3/3: 12
Nombre d'eleves de la classe 3: 4
  Note de l'eleve 1/4: 16
  Note de l'eleve 2/4: 15
  Note de l'eleve 3/4: 12
  Note de l'eleve 4/4: 9

Classe 1: ( 15, 16, 12, 11 )
Classe 2: ( 14, 16, 12 )
Classe 3: ( 16, 15, 12, 9 )

Destruction de e

Classes dans l'ecole: 0
Addresse de e.classes: (nil)