IFT-2000: Structures de données

Slides:



Advertisements
Présentations similaires
GEF 243B Programmation informatique appliquée Listes chaînées I – Tableaux de structures §15.1 – 15.2.
Advertisements

Premier programme en C :
Cours de C – Séance dexercices 12 Octobre Exercice 5 Idem quexercice 1 : lire une ligne au clavier Sans limitation de la longueur de la ligne (utilisez.
Introduction au Langage C,C++
Introduction Langage très répandu Noyau Linux VLC … Des avantages indéniables mais aussi des contraintes ! Ceci nest quun rapide tour.
Rappels C.
GEF 243B Programmation informatique appliquée
C.
Paramètres et pointeurs
Structures et unions types énumérés Qu'est-ce qu'une structure
Les pointeurs Manipulation d'adresses et de ce qui est contenu dans ces adresses Très important, fondamental même en C mauvaise réputation : 'dur à comprendre',
Chap. 1 Structures séquentielles : listes linéaires
FLSI602 Génie Informatique et Réseaux
FLSI602 Génie Informatique et Réseaux
Points importants de la semaine Les fonctions. La portée. La passage par copie. Les tableaux.
Regrouper des éléments de même type et pouvoir y accéder à laide dun identificateur et dun indice. Objectif des tableaux.
Structures de données linéaires
IFT-2000: Structures de Données
8PRO100 Éléments de programmation Allocation dynamique de la mémoire.
La fonction alloue un bloc de taille size. Il faut indiquer la taille du bloc que lon veut allouer. Le premier exemple: #include void main()
Programme de baccalauréat en informatique Algorithmique et programmation IFT-1001 Thierry EUDE Hatem Mahbouli Laboratoire #12 Département dinformatique.
TRAITEMENT DE STRUCTURES
IFT-2000: Structures de Données Listes chaînées Dominic Genest, 2009.
Quest-ce quune classe dallocation? Une classe dallocation détermine la portée et la durée de vie dun objet ou dune fonction.
Les pointeurs Enormément utilisé en C/C++ ! Pourquoi? A quoi ça sert?
Les pointeurs Modes d’adressage de variables. Définition d’un pointeur. Opérateurs de base. Opérations élémentaires. Pointeurs et tableaux. Pointeurs et.
IFT-2000: Structures de données Plan de cours Théorie du contrat Types abstraits Dominic Genest, 2009.
IFT-2000: Structures de données Les graphes Dominic Genest, 2009.
Structures de données IFT Abder Alikacem La classe string Département dinformatique et de génie logiciel Édition Septembre 2009 Département dinformatique.
Structures de données IFT-2000
Structures de données IFT-2000
Sixième cours Les chaînes de caractères et le passage de paramètres par référence Passage de paramètres par référence String.h.
Points importants de la semaine Les allocations programmées de mémoire. Les enregistrements.
8PRO100 Éléments de programmation Les types composés.
IFT-2000: Structures de données Piles et files Dominic Genest, 2009.
Structures de données IFT-2000
IFT-2000: Structures de données Éléments techniques avancés du C et du C++ Dominic Genest, 2009.
Structures de données IFT-2000 Abder Alikacem Retour sur les listes ordonnées Département dinformatique et de génie logiciel Édition Septembre 2009.
Plan cours La notion de pointeur et d’adresse mémoire.
Structures de données IFT-2000
L’essentiel du langage C
Le langage C Structures de données
Le langage C Rappel Pointeurs & Allocation de mémoire.
Les Pointeurs et les Tableaux Statiques et Tableaux Dynamiques
La notion de type revisitée en POO
et quelques rappels sur certains éléments du langage C
Les adresses des fonctions
Les types.
Un survol du language C.
1 Structures des données. 2  Le tableau permettait de désigner sous un seul nom un ensemble de valeurs de même type, chacune d'entre elles étant repérée.
La fonction alloue un bloc de taille size. Il faut indiquer la taille du bloc que l’on veut allouer. Le premier exemple: #include void main()
ETNA – 1ème année Guillaume Belmas –
Les types composés Les enregistrements.
TABLEAUX des POINTEURS TRAITEMENT DE STRUCTURES
8PRO100 Éléments de programmation Les pointeurs de caractères.
Cours LCS N°4 Présenté par Mr: LALLALI
Classe 1 CSI2572 Autres modificateurs de déclaration de variables: & volatile & register & static & auto & extern & const volatile Indique au compilateur.
Conception de Programmes - IUT de Paris - 1ère année Quelques éléments du langage C++ Les références La surcharge de fonctions Les fonctions «
Introduction au langage C Structures de données
8PRO107 Éléments de programmation Les adresses et les pointeurs.
8PRO107 Éléments de programmation Les tableaux. Étude de cas 1 Description du problème : Lire une liste d’entiers et l’afficher d’abord dans le même ordre.
Philippe Gandy - 22 septembre 2015 Basé sur les notes de cours de Daniel Morin et Roch Leclerc.
Exercices sur les pointeurs. lireCar/remettreCar Lorsque l’on lit caractère par caractère, on ne peut pas savoir qu’on a atteint un caractère avant de.
3ième Classe (Mardi, 23 Septembre) CSI2572. O jourd'8: E Allocation de mémoire E Déallocation de mémoire E Tableaux (n dimensions) E Arithmetique des.
C++ BY AURÉLIEN MODULO MARION. PLAN DES TROIS PRÉSENTATIONS C++ avancé C++ orienté objet Bases de C++
Informatique 2A Langage C 5ème séance. Déroulement de la séance 5 1 ère partie Étude des chaînes de caractères 2 ème partie Les structures 3.
Informatique 1A Langage C 6 ème séance 1. Objectifs de la séance 6  Allocation dynamique de mémoire  Application à la création de tableaux 2.
M. BENJELLOUN : 2005 Le but final est de programmer un jeu où l'ordinateur choisira un nombre aléatoire entre 0 et 100 que vous devez deviner.
Transcription de la présentation:

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

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

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 l’accompagner d’une 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

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 l’exemple précédent, c’était 100), alors le tableau n’a plus d’espace pour ajouter quoi que ce soit. Le principal désavantage des tableaux statiques est d’avoir à fixer une telle limite. Dominic Genest, 2009

Tableaux statiques Une autre convention, pour déterminer la fin utilisable d’un tableau statique, est de remplacer l’utilisation d’un nombre entier à côté par une valeur spéciale supplémentaire dans le tableau lui-même, placée à la fin. C’est 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

Tableaux statiques En C En C++ typedef enum { OK, Erreur } CodeErr; typedef struct { int matricule,nb_cours,age; char nom[50]; } Etudiant; 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); struct Etudiant { public: int matricule,nb_cours,age; char nom[50]; }; struct Universite Universite(); void ajout(Etudiant et); int trouve_age(int matricule) const; ~Universite(); private: Etudiant etudiants[40000]; int nb_etudiants; Dominic Genest, 2009

Tableaux statiques En C 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; int trouve_age(const Universite *u, int matricule) int i; for(i=0;i<u->n;i++) if(u->etudiants[i].matricule==matricule) return u->etudiants[i].age; return -1; // Convention pour matricule introuvable. void detruitUniversite(Universite *u) using namespace std; Universite::Universite() { n=0; } void Universite::ajout(Etudiant et) if(n==40000) throw runtime_error(‘’L’université est pleine!’’); etudiants[n++]=et; int Universite::trouve_age(int matricule) const for(int i=0;i<n;i++) if(etudiants[i].matricule==matricule) return etudiants[i].age; return -1; // Convention pour matricule introuvable. Universite::~Universite() Dominic Genest, 2009

Tableaux dynamiques Rappel: Une variable déclarée comme tableau utilisée à elle seule sans les crochets est de type « pointeur » et désigne l’adresse-mémoire du début de ce tableau. On peut lui appliquer toutes les opérations qu’on peut appliquer à un tel pointeur. Inversement, on peut appliquer l’opérateur [] à un pointeur, de sorte qu’on considère le contenu de ce pointeur comme étant l’adresse-mémoire du début d’un 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

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

La fonction « malloc » La fonction « malloc » sert à demander au système d’exploitation un certain espace mémoire, en cours d’exé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 d’octets (donc il faut nous-mêmes multiplier le nombre d’éléments par le nombre d’octets pour chaque élément, à l’aide de l’opérateur sizeof). La fonction retourne l’adresse-mémoire du début du bloc-mémoire alloué, laquelle doit normalement être emmagasinée dans un pointeur. Si jamais le système d’exploitation n’arrive pas à allouer l’espace demandé, la valeur zéro est retournée. Si on veut vérifier tous les cas d’erreurs, il faut donc comparer la valeur retournée avec zéro (ou certains préfèrent la constante NULL, mais c’est exactement la même chose que zéro…) et traiter ce cas spécial avant de continuer. Dominic Genest, 2009

La fonction « free » La fonction free sert à libérer un espace-mémoire précédemment alloué à l’aide de malloc. On doit lui passer en paramètre le début d’un espace-mémoire, lequel doit obligatoirement avoir été retourné par un appel à malloc. On ne peut pas libérer une partie d’un 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 s’avère pratique dans certains cas. Dominic Genest, 2009

La fonction « realloc » #include<math.h> 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 qu’on se rend compte ici qu’on a // besoin de plus d’espace… t = realloc(t,sizeof(float)*14); for(i=10;i<14;i++) t[i]=sin(i*2*M_PI/10); free(t); Dominic Genest, 2009

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 d’octets désiré pour le bloc (qui peut être plus petit ou plus grand). realloc retourne l’adresse-mémoire du début du bloc-mémoire, qui peut très souvent être changé de place lors de l’agrandissement (ou même lors du rapetissement). Il y a deux cas spéciaux d’appels à realloc: Un appel à realloc avec zéro comme nouveau nombre d’octets (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

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

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; void detruitUniversite(Universite *u) free(u->etudiants); Dominic Genest, 2009

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

Réduction du nombre d’allocations 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; void detruitUniversite(Universite *u) free(u->etudiants); typedef enum { OK, Erreur } CodeErr; typedef struct { int matricule,nb_cours,age; char nom[50]; } Etudiant; 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); Dominic Genest, 2009

Chaînes de caractères dynamiques Puisqu’une chaîne de caractères n’est finalement qu’un 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

Copies profondes et copies superficielles Lorsqu’une structure a un membre qui est un pointeur, copier le contenu de la structure d’une variable à une autre avec l’opé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 c’est rarement le cas. Par exemple, si on fait free sur l’une des copies, alors la deuxième copie n’est 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

Tableaux à plusieurs dimensions Puisqu’une 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

Les suppressions CodeErreur supprimer_dernier(Universite *u) { // Très rapide! if(u->n==0) return Erreur; u->n--; if(u->n<=u->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<u->n-1;i++) u->etudiants[i]=u->etudiants[i+1]; return supprimer_dernier(u); } Dominic Genest, 2009

Les suppressions Dans un tableau tel qu’on l’a implanté jusqu’à maintenant, qu’il soit dynamique ou statique, la suppression du premier élément est beaucoup plus lente que la suppression du dernier, puisqu’on doit décaler tous les éléments. On pourrait croire que pour supprimer au début, il suffirait d’incrémenter le pointeur du tableau, mais cela ne fonctionnerait pas car nous perdrions l’adresse-mémoire du début du tableau qui est nécessaire aux appels à « realloc » ou à « free ». En effet, l’adresse-mémoire du début d’un bloc-mémoire alloué dynamiquement fait office d’identifiant 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

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; CodeErreur compacterUniversite(Universite *u) { // Puisqu’on doit utiliser ce code dans deux fonctions, il vaut mieux en faire une fonction utilitaire if(u->debut+u->n<=u->nb_alloues/2) Etudiant *e = realloc(u->etudiants,sizeof(Etudiant)*(u->debut+u->n)); u->nb_alloues = u->debut+u->n; 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) Dominic Genest, 2009

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 s’organisant pour que toutes nos fonctions n’utilisent plus simplement l’indice « 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 »). L’implémentation de l’exemple précédent avec cela est laissée en exercice (remarque: ceci implique qu’il faut réorganiser les éléments lors de l’agrandissement ou de la compaction du tableau). Dominic Genest, 2009