Structures
Annuaire Exercice 4
Réservation de mémoire - 1 -
Accueil

du site

Retour à l'Exercice n°1

Où en sommes-nous ?

Nous avons laissé un problème de côté : celui de l'occupation abusive d'espace mémoire que représente la déclaration d'un fichier sous la forme :

Fiche Fichier[NB_MAX_FICHES];

Il est en effet possible de réserver de la mémoire progressivement, au fur et à mesure des besoins.

C'est la réservation dynamique de mémoire.

Attention ! cette opération est de la compétence du système d'exploitation de votre ordinateur.
Heureusement pas de la nôtre car ce n'est pas simple ...

L'OS (Operating System) ou "système d'exploitation" doit :

  • veiller à ce qu'il y ait assez de mémoire compte tenu de la taille de la variable que vous voulez réserver.
  • s'il n'y en a pas assez il faut qu'il vous le dise sans "planter" l'ordinateur.
  • l'espace mémoire est partagé entre votre application et les autres applications que vous avez lancées et qui tournent en tâche de fond : il faut savoir où tout ce monde se situe et qui possède quoi.
  • Il arrive qu'il n'y ait pas assez de mémoire physique RAM pour continuer.
    L'OS se livre alors à un exercice périlleux qui consiste à enregistrer provisoirement sur disque une partie de la mémoire non immédiatement utilisée afin de pouvoir "caser" dans la RAM des segments de mémoire pour les données demandée à ce moment. C'est ce qu'on appelle le mécanisme de mémoire virtuelle.
    Où sont vos donnés dans tout çà ?
    L'OS seul le sait !

Il vaut mieux laisser à l'OS cette gestion compliquée (d'ailleurs on ne peut pas faire autrement).

Nous disposons pour cela de deux opérateurs (ce ne sont pas des fonctions) :

  • new pour demander à l'OS de vous réserver de la mémoire (et pour vous dire où il l'a réservée)
  • delete pour libérer la mémoire précédemment réservée

Et, aussi de deux fonctions :

  • malloc()
  • free()
Liens vers la description de ces opérateurs & procédures
new & delete
malloc & free
Pour revenir ici : cliquer sur "Exemple d'application"
dans ces pages descriptives

Ci-dessous, la suite de notre programme en utilisant les opérateur nex et delete


Programme principal modifié : Annuaire.cpp
//*************************************************************
//Programme "ANNUAIRE"
//FICHIER Annuaire.cpp (Version 4)
//Programme principal
//*************************************************************

#include <stdio.h>
//Prototypes pour printf() scanf() getchar()

#include "Fiche.h"
//Prototypes pour AjoutFiche() - VisuFichier() -
//InitialisationFiche()- FinalisationFiche()

int main()
{
 char entree;

 InitialisationFiche();

 //Boucle d'invite :
 //---------------

 do
 {
  printf("\n\n Annuaire \n");
  printf("=========================\n");
  printf("Choix a votre disposition\n");
  printf("Saisir une fiche : S\n");
  printf("Visualiser le fichier : V\n");
  printf("Quitter : Q\n");
  printf("Entrer votre choix : ");

  scanf("%c",&entree);
  rewind(stdin);

  switch(entree)
  {
   case 'S' :
   case 's' :
   AjoutFiche();
   break;

   case 'V' :
   case 'v' :
   VisuFichier();
   break;

   case 'Q' :
   case 'q' :
   printf("\nSaisie & Visualisation terminees.\n");
   break;

   default : printf("\nEntrez S, V ou Q !\n");
   break;

 
}
 }while((entree != 'Q')&&(entree != 'q'));

 FinalisationFiche();

 printf("Au revoir et merci de nous avoir fait confiance.\n");
 getchar();
 return 0;
}


Commentaires sur le nouveau programme principal

Les seules références à tout ce qui touche au fichier et aux fiches de ce fichier sont les 4 procédures :

InitialisationFiche()
FinalisationFiche()
AjoutFiche()
VisuFiche()

Notre souci est de vous amener progressivement à la notion d'objet
(programmation orientée objet)

Plus tard nous appelleons l'ensemble des données et des procédures relatives à "Fiche" : l'objet "Fiche"

Cet objet "Fiche" sera toujours défini dans deux fichiers :

  • l'un "Fiche.cpp" où sont écrites et définies toutes les procédures et les variables relatives à Fiche
  • l'autre "Fiche.h" où sont déclarés les prototypes des fonctions et des variables utilisables par d'autres programmes.
  • Les procédures et les variables utilisables par les autres programmes s'appelleont "publiques"
  • Les procédures et variables utilisables en interne par "Fiche.cpp" sont dites "privées" et ne doivent pas apparaître dans "Fiche.h"

On a ainsi voulu empêcher l'utilisateur de l'objet "Fiche" de modifier cet objet : la notion de programmation objet a beaucoup apporté à la sécurité de programmation.

Attention : nous ne faisons pas encore de programmation orintée objet : nous en amorçons seulement le concept.


Constructeurs & destructeurs

En programmation orientée objet on déclare toujours un objet de même qu'en programmation classique on déclare une variable.

Mais un objet est plus complexe q'une variable, il a besoin d'une procédure d'initialisation que l'on appelle "Constructeur", systématiquement appelée "Fiche()".
Notre procédure "InitialisationFiche()" tient lieu de constructeur.

//*************************************************************
//Programme "ANNUAIRE"
//Fiche.cpp (Version 4)
//*************************************************************
#include "Fiche.h"
//...........................

bool InitialisationFiche()
{
 if(FichierExiste("FichierAnnuaire.apm"))
 {
  nbFiches = NombreDeFiches("FichierAnnuaire.apm") ;

  //Allocation dynamique de mémoire
  Fichier = new Fiche [nbFiches];
  if(Fichier==NULL) return false;
  LitFichier("FichierAnnuaire.apm",Fichier,nbFiches);
 }
 else
 {
  nbFiches = 0;
  //Attention, Fichier ne pointe sur rien
  //Il sera pointé dans "AjoutFiche"
 }
 return true;
}
//...........................

Commentaire :

Fichier = new Fiche [nbFiches];
 Voir les règles de réservation mémoire par "new"
 si le pointeur "Fichier" retourné a la valeur "NULL"
 c'est que l'O.S. n'a pu réserver de la mémoire.
 La procédure s'arrête et retourne "false"


Commentaire sur la procédure :
NombreDeFiches("FichierAnnuaire.apm");

//*************************************************************
//Programme "ANNUAIRE"
//Fiche.cpp (Version 4)
//*************************************************************
#include "Fiche.h"
//...........................

unsigned NombreDeFiches(char * pszNomFichier)
{
 FILE * pAF;
 Fiche F;
 unsigned nb;

 //Ouverture du fichier pour y lire (r = read)
 if ((pAF = fopen( pszNomFichier, "r" )) == NULL )
 return 0;

 nb=0;

 while(fread(&F,sizeof(Fiche),1,pAF)) //Tant que pas fin de  fichier
 {
  if(! FicheVide(F))
  nb++;
  else
  break;
 }

 fclose(pAF);
 return nb;
}

//...........................

Tant la valeur retournée par fread(...) - qui n'est autre que le nombre de structures réelement lues - n'est pas nulle on continue à lire. Quand elle est nulle on est en fin de fichier : on s'arrête de lire.

On nous dit souvent qu'il aurait fallu écrire :

while(fread(&F,sizeof(Fiche),1,pAF) != 0)

mais cela revient au même que :

while(fread(&F,sizeof(Fiche),1,pAF))

car while(variable ) évalue "variable" en tant que booléen.
Si "variable" est nulle : il en déduit "false"
Si "variable" n'est pas nulle : il en déduit "no false" = "true"


Commentaire sur la fonction "FinalisationFiche" - notre destructeur.

//*************************************************************
//Programme "ANNUAIRE"
//Fiche.cpp (Version 4)
//*************************************************************
#include "Fiche.h"
//...........................

void FinalisationFiche()
{
 EcritFichier("FichierAnnuaire.apm",Fichier,nbFiches);
 delete [] Fichier;
}

//...........................

EcritFichier enregistre le fichier sur le disque
delete libère la mémoire désormais inutile puisqu'on sont du programme.


Procédure AjoutFiche(...)
//*************************************************************
//Programme "ANNUAIRE"
//Fiche.cpp (Version 4)
//*************************************************************
#include "Fiche.h"
//...........................

bool AjoutFiche()
{
 int i;

 // 1 - Créer un nouveau tableau de n+1 fiches
 Fiche * pTableau2 = new Fiche[nbFiches+1];
 if(pTableau2 == NULL) return false;

 // 2 - Copier l'ancien tableau dans le nouveau
 if(nbFiches !=0)
 {
  for(i=0; i< nbFiches; i++)
  CopieFiche(pTableau2[i], Fichier[i]);
  // 3 - Libérer l'ancien tableau
  delete[] Fichier;
 }
 else
 RazFiche(pTableau2[0]);

 // 4 - Faire pointer l'ancien pointeur
 //     sur le nouveau tableau
 Fichier = pTableau2;

 SaisieFiche(Fichier[nbFiches++]);
 // nbFiches sera incrémenté
 // après saisie de la dernière
 return true;

}

//...........................


Fiche.h
//************************************************************
//Programme "ANNUAIRE"
//Fiche.h (Version 4)
//************************************************************
//************************************************************
//Programme "ANNUAIRE"
//FICHIER Fiche.h (Fiche.h version 4)
//Définition des variables rattachées aux fiches de l'annuaire
//Prototypes des fonctions associées
//************************************************************

//============================================================
//Définition d'une structure de fiche pour l'annuuaire
//------------------------------------------------------------
#define CAR_MAX_NOM 64
#define CAR_MAX_DATE 18
#define CAR_MAX_ADRESSE 128
#define CAR_MAX_OBSERV 128
#define CAR_MAX_TEL 20
#define CAR_MAX_COURRIEL 64

#define NB_MAX_FICHES 100

typedef struct
{
 char Nom [CAR_MAX_NOM + 1];
 char Prenom[CAR_MAX_NOM + 1];
 char Sexe; //'F' ou 'M'
 char DateNaissance[CAR_MAX_DATE + 1];
 char Adresse [CAR_MAX_ADRESSE + 1];
 char Ville [CAR_MAX_NOM + 1];
 int CodePostal;
 char TelDom [CAR_MAX_TEL + 1];
 char TelMobile[CAR_MAX_TEL + 1];
 char TelBureau[CAR_MAX_TEL + 1];
 char Courriel [CAR_MAX_COURRIEL + 1];
 bool Cadre;
 double Taille; //Unité à préciser : le mètre.
 char Observations[CAR_MAX_OBSERV + 1];
}Fiche;

//N.B. : les +1 à cause des terminateurs de chaîne (0)
//============================================================

//============================================================
//Prototypes des fonctions définies dans "Fiche.cpp"

//Fonctions privées
//------------------------------------------------------------
void SaisieFiche(Fiche *);
void SaisieFiche(Fiche &);

void VisuFiche(Fiche *);
void VisuFiche(Fiche &);
void RazFiche(Fiche &F);
bool FicheVide(Fiche &F);
int  FicheVide(Fiche *pF);
void RazFichier(Fiche * pF);
void CopieFiche(Fiche &Fdest,Fiche& Fsce);

bool SaisieFichier(Fiche *);
void VisuFichier(Fiche * pF);
void VisuFichier(Fiche * pF,int nbFiches);
bool FichierPlein(Fiche *pF);

bool AjoutFiche(Fiche ** ppFichier);

//Fonctions publiques
//-------------------
bool InitialisationFiche();
void FinalisationFiche();
void VisuFichier();
bool AjoutFiche();

//============================================================
//Procédures relatives au stockage persistant des données
//============================================================
bool FichierExiste(char * pszNomFichier);
bool EcritFichier(char * pszNomFichier, Fiche *pFichier, unsigned nbFiches);
bool LitFichier(char * pszNomFichier, Fiche *pFichier, unsigned nbFiches);
unsigned NombreDeFiches(char * pszNomFichier);


Dans "Annuaire5" qui suit, nous montrons une version du même programme
en utilisant cette fois les fonctions de réservation de mémoire
malloc() et free()

Retour à Annuaire 3
La suite d'Annuaire 4
Retour à Annuaire 1
Sommaire Langage C
Accueil

du site