La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

IFT-2000: Structures de données Tableaux Dominic Genest, 2009.

Présentations similaires


Présentation au sujet: "IFT-2000: Structures de données Tableaux Dominic Genest, 2009."— Transcription de la présentation:

1 IFT-2000: Structures de données Tableaux Dominic Genest, 2009

2 Les tableaux Tableaux – Statiques – Chaînes de caractères – Dynamiques – À plusieurs dimensions Dominic Genest, 2009

3 Tableaux statiques float x[100]; x[0]=4.5f; x[1]=2.3f; printf(« %f »,x[0]); printf(« %f »,x[1]); printf(« %f »,x[2]); // ? Quand on utilise un tableau statique pour emmagasiner un nombre indéterminé de données, il faut laccompagner dune variable de type « int » pour savoir combien de données sont vraiment considérées comme faisant partie de notre structure. La taille du tableau lui-même est une limite maximale. typedef struct { float x[100]; int n; } Nombres; CodeErreur initNombres(Nombres *n); CodeErreur ajoutNombres(Nombres *n, float nouv_nbr); // etc. En C++: struct Nombres { Nombres(); void ajout(float nouv_nbr); // etc. private: float x[100]; int n; }; Dominic Genest, 2009

4 Tableaux statiques Quand « n » vaut zéro, on considère que le tableau est vide. Quand on initialise le tableau, on met 0 dans n. Quand on ajoute un élément, on incrémente ce n. Quand on en supprime un, on le décrémente. Quand on fait une recherche, on ne traite comme candidats que les éléments de 0 à n-1. Quand n vaut la taille allouée entre les crochets (dans lexemple précédent, cétait 100), alors le tableau na plus despace pour ajouter quoi que ce soit. Le principal désavantage des tableaux statiques est davoir à fixer une telle limite. Dominic Genest, 2009

5 Tableaux statiques Une autre convention, pour déterminer la fin utilisable dun tableau statique, est de remplacer lutilisation dun nombre entier à côté par une valeur spéciale supplémentaire dans le tableau lui-même, placée à la fin. Cest la convention adoptée pour les chaînes de caractères. Le caractère zéro est placé à la fin de toute chaîne de caractères en guise de marqueur de fin. Ce caractère est reconnu par les fonctions « strlen », « strcpy », « strcat », et compagnie. Dominic Genest, 2009

6 Tableaux statiques En C typedef enum { OK, Erreur } CodeErr; typedef struct { int matricule,nb_cours,age; char nom[50]; } Etudiant; typedef struct { Etudiant etudiants[40000]; int n; } Universite; CodeErr initUniversite(Universite *u); CodeErr ajoutUniversite(Universite *u, Etudiant et); int trouve_age(const Universite *u, int matricule); void detruitUniversite(Universite *u); En C++ struct Etudiant { public: int matricule,nb_cours,age; char nom[50]; }; struct Universite { public: Universite(); void ajout(Etudiant et); int trouve_age(int matricule) const; ~Universite(); private: Etudiant etudiants[40000]; int nb_etudiants; }; Dominic Genest, 2009

7 Tableaux statiques En C CodeErr initUniversite(Universite *u) { u->n=0; return OK; } CodeErr ajoutUniversite(Universite *u, Etudiant et) { if(u->n==40000) return Erreur; u->etudiants[u->n++] = et; return OK; } int trouve_age(const Universite *u, int matricule) { int i; for(i=0;i n;i++) if(u->etudiants[i].matricule==matricule) return u->etudiants[i].age; return -1; // Convention pour matricule introuvable. } void detruitUniversite(Universite *u) { } En C++ using namespace std; Universite::Universite() { n=0; } void Universite::ajout(Etudiant et) { if(n==40000) throw runtime_error(Luniversité est pleine!); etudiants[n++]=et; } int Universite::trouve_age(int matricule) const { for(int i=0;i

8 Tableaux dynamiques Rappel: Une variable déclarée comme tableau utilisée à elle seule sans les crochets est de type « pointeur » et désigne ladresse-mémoire du début de ce tableau. On peut lui appliquer toutes les opérations quon peut appliquer à un tel pointeur. Inversement, on peut appliquer lopérateur [] à un pointeur, de sorte quon considère le contenu de ce pointeur comme étant ladresse-mémoire du début dun tableau auquel on veut accéder. float t[20]; float *p; p=t; p[4]=2.3f; printf(« %f »,t[4]); // Ceci affiche 2.3 Dominic Genest, 2009

9 Tableaux dynamiques En C float *p; p = malloc(sizeof(float)*20); p[4]=2.3f; printf(%f,p[4]); free(p); En C++ float *p; p = new float[20]; p[4]=2.3f; printf(%f,p[4]); delete[] p; Dominic Genest, 2009

10 La fonction « malloc » La fonction « malloc » sert à demander au système dexploitation un certain espace mémoire, en cours dexécution du programme. Son avantage est que la taille de ce bloc-mémoire peut ne pas encore être déterminée à la compilation. La fonction prend un paramètre qui est un nombre doctets (donc il faut nous-mêmes multiplier le nombre déléments par le nombre doctets pour chaque élément, à laide de lopérateur sizeof). La fonction retourne ladresse-mémoire du début du bloc-mémoire alloué, laquelle doit normalement être emmagasinée dans un pointeur. Si jamais le système dexploitation narrive pas à allouer lespace demandé, la valeur zéro est retournée. Si on veut vérifier tous les cas derreurs, il faut donc comparer la valeur retournée avec zéro (ou certains préfèrent la constante NULL, mais cest exactement la même chose que zéro…) et traiter ce cas spécial avant de continuer. Dominic Genest, 2009

11 La fonction « free » La fonction free sert à libérer un espace-mémoire précédemment alloué à laide de malloc. On doit lui passer en paramètre le début dun espace-mémoire, lequel doit obligatoirement avoir été retourné par un appel à malloc. On ne peut pas libérer une partie dun bloc- mémoire alloué. On doit toujours le libérer en entier. Si on passe la valeur zéro à free, il ne se passe rien. Cela savère pratique dans certains cas. Dominic Genest, 2009

12 La fonction « realloc » #include int i; float *t; t = malloc(sizeof(float)*10); If(t==0) { printf(Pas assez de mémoire.); exit(-1); } for(i=0;i<10;i++) t[i]=sin(i*2*M_PI/10); // Disons quon se rend compte ici quon a // besoin de plus despace… t = realloc(t,sizeof(float)*14); If(t==0) { printf(Pas assez de mémoire.); exit(-1); } for(i=10;i<14;i++) t[i]=sin(i*2*M_PI/10); free(t); Dominic Genest, 2009

13 La fonction « realloc » La fonction realloc prend deux paramètres: une adresse- mémoire obligatoirement retournée par un appel précédent à malloc (ou à realloc), puis un nouveau nombre doctets désiré pour le bloc (qui peut être plus petit ou plus grand). realloc retourne ladresse-mémoire du début du bloc- mémoire, qui peut très souvent être changé de place lors de lagrandissement (ou même lors du rapetissement). Il y a deux cas spéciaux dappels à realloc: – Un appel à realloc avec zéro comme nouveau nombre doctets (deuxième paramètre) est équivalent à un appel à « free ». – Un appel à realloc avec zéro comme adresse-mémoire (premier paramètre) est équivalent à un appel à « malloc ». Dominic Genest, 2009

14 Tableaux dynamiques typedef enum { OK, Erreur } CodeErr; typedef struct { int matricule,nb_cours,age; char nom[50]; } Etudiant; typedef struct { Etudiant *etudiants; int n; } Universite; CodeErr initUniversite(Universite *u); CodeErr ajoutUniversite(Universite *u, Etudiant et); int trouve_age(const Universite *u, int matricule); void detruitUniversite(Universite *u); CodeErr initUniversite(Universite *u) { u->n=0; return OK; } CodeErr ajoutUniversite(Universite *u, Etudiant et) { Etudiant *e; if(u->n==0) { u->etudiants = malloc(sizeof(Etudiant)); if(!u->etudiants) return Erreur; u->etudiants[0]=et; u->n=1; return OK; } e = realloc(u->etudiants,sizeof(Etudiant)*(u->n+1)); if(!e) return Erreur; u->etudiants = e; u->etudiants[u->n++] = et; return OK; } void detruitUniversite(Universite *u) { if(u->n>0) free(u->etudiants); } Dominic Genest, 2009

15 Simplification grâce aux cas particuliers de « realloc » avec zéro CodeErr initUniversite(Universite *u) { u->n=0; u->etudiants=0; return OK; } CodeErr ajoutUniversite(Universite *u, Etudiant et) { Etudiant *e = realloc(u->etudiants,sizeof(Etudiant)*(u->n+1)); if(!e) return Erreur; u->etudiants = e; u->etudiants[u->n++] = et; return OK; } void detruitUniversite(Universite *u) { free(u->etudiants); } Dominic Genest, 2009

16 Remarques sur « malloc », « free » et « realloc » Les appels à ces trois fonctions sont très lents et on doit sorganiser pour en faire un minimum. Une implémentation comme lexemple précédent est très peu performante car elle implique un appel à « realloc » lors de chaque ajout. Il est préférable dagrandir le tableau plus rarement que les ajouts; cela se fait en réservant à lavance de la place dans le tableau. Dominic Genest, 2009

17 Réduction du nombre dallocations typedef enum { OK, Erreur } CodeErr; typedef struct { int matricule,nb_cours,age; char nom[50]; } Etudiant; typedef struct { Etudiant *etudiants; int n; int nb_alloues; } Universite; CodeErr initUniversite(Universite *u); CodeErr ajoutUniversite(Universite *u, Etudiant et); int trouve_age(const Universite *u, int matricule); void detruitUniversite(Universite *u); CodeErr initUniversite(Universite *u) { u->n=0; u->etudiants=0; u->nb_alloues=0; return OK; } CodeErr ajoutUniversite(Universite *u, Etudiant et) { if(u->n==u->nb_alloues) {// On double la taille du tableau Etudiant *e = realloc(u- >etudiants,sizeof(Etudiant)*((u->n+1)*2)); if(!e) return Erreur; u->etudiants = e; u->nb_alloues = (u->n+1)*2; } u->etudiants[u->n++] = et; return OK; } void detruitUniversite(Universite *u) { free(u->etudiants); } Dominic Genest, 2009

18 Chaînes de caractères dynamiques Puisquune chaîne de caractères nest finalement quun tableau de caractères, on peut en faire une version dynamique. char *x; x = malloc(sizeof(char)*(strlen(Bonjour)+1)); if(!x) { …erreur… } strcpy(x, Bonjour); printf(%s,x); free(x); Dominic Genest, 2009

19 Copies profondes et copies superficielles Lorsquune structure a un membre qui est un pointeur, copier le contenu de la structure dune variable à une autre avec lopérateur « = » ne va que copier le contenu du pointeur en question. Ainsi, les deux variables partageront un même espace-mémoire. Cela peut être désirable dans certains cas particuliers (si on implémente un index, par exemple), mais cest rarement le cas. Par exemple, si on fait free sur lune des copies, alors la deuxième copie nest plus utilisable non plus et fera planter le logiciel. typedef struct { int matricule,age,nb_cours; char *nom; } Etudiant; Etudiant a,b; a.nom = malloc(sizeof(char)*(strlen(Dominic)+1)); strcpy(a.nom,Dominic); b = a; // Dangereux! Dominic Genest, 2009

20 Tableaux à plusieurs dimensions Puisquune chaîne de caractères est un tableau, si on veut faire un tableau de chaînes de caractères, on a alors à faire à un tableau de tableaux. On a le choix entre: – Un tableau statique de chaînes de caractères statiques – Un tableau dynamique de chaînes de caractères statiques – Un tableau statique de chaînes de caractères dynamiques – Un tableau dynamique de chaînes de caractères dynamiques Dominic Genest, 2009

21 Les suppressions CodeErreur supprimer_dernier(Universite *u) {// Très rapide! if(u->n==0) return Erreur; u->n--; if(u->n nb_alloues/2) {// On compacte le tableau Etudiant *e = realloc(u- >etudiants,sizeof(Etudiant)*(u->n)); if(!e) return Erreur; u->etudiants = e; u->nb_alloues = u->n; } return OK; } CodeErreur supprimer_premier(Universite *u) {// Beaucoup plus long! int i; if(u->n==0) return Erreur; for(i=0;i n-1;i++) u->etudiants[i]=u- >etudiants[i+1]; return supprimer_dernier(u); } Dominic Genest, 2009

22 Les suppressions Dans un tableau tel quon la implanté jusquà maintenant, quil soit dynamique ou statique, la suppression du premier élément est beaucoup plus lente que la suppression du dernier, puisquon doit décaler tous les éléments. On pourrait croire que pour supprimer au début, il suffirait dincrémenter le pointeur du tableau, mais cela ne fonctionnerait pas car nous perdrions ladresse-mémoire du début du tableau qui est nécessaire aux appels à « realloc » ou à « free ». En effet, ladresse-mémoire du début dun bloc-mémoire alloué dynamiquement fait office didentifiant de ce bloc-mémoire. On peut par contre déclarer un troisième nombre entier dans la structure, celui-ci permettant de faire varier le début logique du tableau. Il faut alors ajuster toutes les fonctions en conséquence. Dominic Genest, 2009

23 Optimisation de la suppression du premier élément grâce à un début variable. typedef struct { Etudiant *etudiants; int n,nb_alloues,debut; } Universite; CodeErreur initUniversite(Universite *u) { u->etudiants=0; u->n=0; u->nb_alloues=0; u->debut=0; return OK; } CodeErreur ajoutUniversite(Universite *u, Etudiant et) { if(u->debut+u->n==u->nb_alloues) { Etudiant *e = realloc(u->etudiants,sizeof(Etudiant)*(u- >debut+u->n+1)*2); if(!e) return Erreur; u->etudiants = e; u->nb_alloues=(u->n+1)*2; } u->etudiants[u->debut+u->n++] = et; return OK; } CodeErreur compacterUniversite(Universite *u) { // Puisquon doit utiliser ce code dans deux fonctions, il vaut mieux en faire une fonction utilitaire if(u->debut+u->n nb_alloues/2) { Etudiant *e = realloc(u->etudiants,sizeof(Etudiant)*(u- >debut+u->n)); if(!e) return Erreur; u->etudiants = e; u->nb_alloues = u->debut+u->n; } return OK; } CodeErreur supprimer_premier(Universite *u, Etudiant et) { if(u->n==0) return Erreur; u->debut++; u->n--; return compacterUniversite(u); } CodeErreur supprimer_dernier(Universite *u, Etudiant et) { if(u->n==0) return Erreur; u->n--; return compacterUniversite(u); } Dominic Genest, 2009

24 Tableaux circulaires Si on fait varier le début, on peut se retrouver à gaspiller un espace important avant le début. On peut utiliser cet espace en sorganisant pour que toutes nos fonctions nutilisent plus simplement lindice « u->debut+u->n », mais plutôt « (u->debut+u- >n)%u->nb_alloues » (rappel: le symbole « % » veut dire « modulo », soit « reste de la division entière »). Limplémentation de lexemple précédent avec cela est laissée en exercice (remarque: ceci implique quil faut réorganiser les éléments lors de lagrandissement ou de la compaction du tableau). Dominic Genest, 2009


Télécharger ppt "IFT-2000: Structures de données Tableaux Dominic Genest, 2009."

Présentations similaires


Annonces Google