Docstoc

Pointeur+et+Liste+Chainée

Document Sample
Pointeur+et+Liste+Chainée Powered By Docstoc
					Pointeurs et gestion dynamique de la mémoire

  Un pointeur est une variable spéciale qui peut contenir l'adresse d'une
  autre variable.


  Remarque
  Les pointeurs et les noms de variables ont le même rôle: Ils donnent
  accès à un emplacement dans la mémoire interne de l'ordinateur.
  Il faut bien faire la différence:

  * Un pointeur est une variable qui peut 'pointer' sur différentes
  adresses.
  * Le nom d'une variable reste toujours lié à la même adresse.




Exemple

  Soit A une variable contenant la valeur 10 et P un pointeur qui
     contient l'adresse de A.
  En mémoire, A et P peuvent se présenter comme suit:




                                                                            1
Déclarations de variables de type
pointeur

Exemple :
int *pi;        /* pi est un pointeur vers un int */
short int *psi;         /* psi est un pointeur vers un short int */
   Type de pointeur générique
 Le type void * est le type pointeur générique, c'est à dire capable de
   pointer vers n'importe quel type d'objet   .




Les opérateurs de base
   Opérateur adresse de
int i;
int *pi;         /* pi pointeur vers un int */
pi = &i;        /* le pointeur pi repère la variable i */
   Opérateur d'indirection
                    Pi              j      i
exemple :
               3F0B
int i, j;                           3 2
int *pi;       3F06        3F08      3F0A 3F0B
pi = &i; /* initialisation du pointeur pi */
*pi = 2; /* initialisation de la valeur pointée par pi */
j = *pi + 1; /* une utilisation de la valeur pointée par pi */




                                                                          2
Pointeurs et tableaux
   Exemple
En déclarant un tableau A de type int et un pointeur P sur int,
int A[10]; int *P;
 l'instruction: P = A;          P = &A[0];
Si P pointe sur une composante quelconque d'un tableau, alors P+1
     pointe sur la composante suivante.
P+i pointe sur la i-ième composante derrière P
 P-i pointe sur la i-ième composante devant P.
Ainsi, après l'instruction,
P = A;
*P        désigne le contenu de A[0]
*(P+1) désigne le contenu de A[1]
*(P+2) désigne le contenu de A[2]
     ...
     ...
*(P+i) désigne le contenu de A[i]




Arithmétique des pointeurs
   Affectation par un pointeur sur le même type
Soient P1 et P2 deux pointeurs sur le même type de données
P1 = P2; /*fait pointer P1 sur le même objet que P2 */

    Addition et soustraction d'un nombre entier
Si P pointe sur l'élément A[i] d'un tableau, alors
   P+n pointe sur A[i+n]
   P-n pointe sur A[i-n]

   Incrémentation et décrémentation d'un pointeur
Si P pointe sur l'élément A[i] d'un tableau, alors après l'instruction
   P++; P pointe sur A[i+1]
   P+=n; P pointe sur A[i+n]
   P--; P pointe sur A[i-1]
   P-=n; P pointe sur A[i-n]




                                                                         3
Arithmétique des pointeurs
    Soustraction de deux pointeurs
 Soient P1 et P2 deux pointeurs qui pointent dans le même tableau:
 P1-P2 fournit le nombre de composantes comprises entre P1 et P2.
    Le résultat de la soustraction P1-P2 est
    - négatif, si P1 précède P2
    - zéro, si P1 = P2
    - positif, si P2 précède P1
    - indéfini, si P1 et P2 ne pointent pas dans le même tableau

    Comparaison de deux pointeurs
 On peut comparer deux pointeurs par <, >, <=, >=, ==, !=.
 La comparaison de deux pointeurs qui pointent dans le même
    tableau est équivalente à la comparaison des indices
    correspondants. (Si les pointeurs ne pointent pas dans le même
    tableau, alors le résultat est donné par leurs positions relatives
    dans la mémoire).




Remarque

    Attention !
 Il existe toujours une différence essentielle entre
    un pointeur et le nom d'un tableau:

 - Un pointeur est une variable,
    donc des opérations comme P = A ou P++
    sont permises.
 - Le nom d'un tableau est une constante,
    donc des opérations comme A = P ou A++
    sont impossibles.




                                                                         4
Exemple

 Les deux programmes suivants copient les éléments positifs d'un tableau T
    dans un deuxième tableau POS.
    Formalisme tableau
 main()
 {
 int T[10] = {-3, 4, 0, -7, 3, 8, 0, -1, 4, -9};
 int POS[10];
  int I,J; /* indices courants dans T et POS */
 for (J=0,I=0 ; I<10 ; I++)
 if (T[I]>0) {
 POS[J] = T[I];
 J++;
 }
 return 0;
 }




Exemple
     Formalisme pointeur
 main()
 {
  int T[10] = {-3, 4, 0, -7, 3, 8, 0, -1, 4, -9};
  int POS[10];
  int i,J; /* indices courants dans T et POS */
 for (J=0,i=0 ; i<10 ; i++)
 if (*(T+i)>0)
 {
 *(POS+J) = *(T+i);
 J++;
 }
 return 0;
 }




                                                                             5
Pointeurs et chaînes de caractères
    Affectation
 a) On peut attribuer l'adresse d'une chaîne de caractères constante à un
    pointeur sur char:
 Exemple
 char *C;
 C = "Ceci est une chaîne de caractères constante";

     Initialisation
 b) char *B = "Bonjour !"; Attention !
 Il existe une différence importante entre les deux déclarations:
 char A[] = "Bonjour !"; /* un tableau */
 char *B = "Bonjour !"; /* un pointeur */
 A est un tableau qui a exactement la grandeur pour contenir la chaîne de
     caractères et la terminaison '\0'. Les caractères de la chaîne peuvent être
     changés, mais le nom A va toujours pointer sur la même adresse en
     mémoire.
 B est un pointeur : pointe sur une chaîne de caractères constante
 La chaîne constante peut être lue, copiée ou affichée, mais pas modifiée.




Pointeurs et structures
     On peut déclarer des pointeurs sur des structures
     nécessaire lorsque la structure est un paramètre modifiable
     dans la fonction.
     Utilisation du symbole ->
     Exemple: typedef struct /* On définit un type struct */
  {
  char nom[10];
  char prenom[10];
  int age;         Déclaration:            fiche *f;
  float note;      Utilisation:            strcpy(f->nom,"DUPONT");
  }                                        strcpy(f->prenom,"JEAN");
  fiche;                                   f->age = 20;
                                           f->note = 11.5;




                                                                                   6
Allocation dynamique de la mémoire

 Pour allouer de l'espace dynamiquement:
 fonction malloc

 Exemple:

 #include <stdlib.h>
 struct personne *p;
 p = malloc(sizeof(struct personne));

  fonction calloc : deux paramètres
 • le premier est le nombre d‘éléments désirés ;
 • le second est la taille en octets d'un élément.

 Exemple:




Allocation dynamique de la mémoire

 #include <stdlib.h>
 struct personne *p;
 int nb_elem;
 ... /* init de nb_elem */
 p = calloc(nb_elem,sizeof(struct personne));
      On peut alors utiliser les éléments p[0], p[1], ... p[nb_elem-1].

 Liberation d'espace : procedure free
 Exemple:
 #include <stdlib.h>
 struct personne *p;
 p = malloc(sizeof(struct personne));
 ... /* utilisation de la structure allouée */
 free(p);




                                                                          7
Les listes chaînées

   Définition:
  C’est une structure dynamique composée de plusieurs
   nœuds chaque nœud comprend :
      des champs de données
      Un pointeur sur le nœud suivant

  tête

             .          .          .




Les listes chaînées
 Chaque élément de la liste est formé de n+1 champs :
 n champs constituant l’information portée par le noeud,
 selon le problème considéré,
 un champ supplémentaire qui est la matérialisation de la
 relation successeur.
 Le dernier élément est signalé par une valeur
 conventionnelle du champ successeur (ex. : Nil ou NULL).
 On accède à la liste par l’adresse de son premier élément
 (tête de liste).




                                                             8
Définition

     En algo                             En C

  Type Liste = pointeur sur noeud;
                                      struct noeud
   noeud =enregistrement
                                      { int valeur;
  valeur : entier
                                         struct noeud * suiv;
  suiv : pointeur sur noeud
  Fin                                 };
                                       struct noeud * p;




Faut-il utiliser un tableau ou une liste
chaînée ?
 Avantages des tableaux par rapport aux listes :
    Accès direct.
    dans une liste chaînée ce temps est de la forme k × i (car il faut
    exécuter i fois l’opération p = p->suiv).
    Pas de sur-encombrement: il n’y a pas besoin d’espace
    supplémentaire pour le codage de l’élément suivant
 Avantages des listes par rapport aux tableaux :
    Liberté dans la définition de la relation successeur (les éléments
    des listes chaînées peuvent être réarrangés sans avoir à
    déplacer les informations qu’ils portent).
    un même élément peut faire partie de plusieurs listes.
    Le nombre de nœuds d’une liste correspond au nombre
    d’éléments effectivement présents




                                                                         9
Exemple

   Création d’une liste.
  Écrivez un programme qui lit une suite de
   nombres positifs tapés au clavier et crée la
   liste chaînée correspondante, dans les deux
   cas suivants :
    1- Le dernier élément lu sera premier dans la liste
      (création par insertion en tête de liste)
    2- La liste doit traduire l’ordre de lecture des nombres
      (création par insertion en queue de liste)




Algorithme (1)
Procedure creation1 ( E/S L: Liste )
Var
p : Liste
Debut
  L      nil
    lire (x)
    tant que (x >= 0) faire
         p     nouveau(enregistrement nœud)
         p^.valeur     x;
         p^.suiv    L;
         L p;
         lire(x)
    fintantque
Fin




                                                               10
Algorithme (2)
Procedure creation2 (L:Liste)
var
p, queue : Liste
x          : entier
Debut                                 queue    p;
 L     Nil                            Lire(x)
 lire (x)                           Fintantque
 tant que (x>=0) faire
     p nouveau(enregistrement nœud) Fin
     p^.valeur      x
     p^.suiv     Nil
     si (L = Nil) alors
       L    p
     sinon
     queue^.suiv      p;
  finsi




Insertion en tête de liste
 #include <stdio.h>
 #include <stdlib.h>
 Typedef struct noeud { int valeur;
                          struct noeud *suivant;
                         } NODE;
 void main()
 { int x;
 NODE *p, *tete=NULL;
 for (;;) {
 printf(« entrer un entier positif ");
 scanf("%d", &x);
 if (x < 0) break;




                                                    11
 p = malloc(sizeof(NODE));
 if (p == NULL) {
 printf("Problème d'allocation de mémoire\n");
 exit(-1); }
  p->valeur = x;
 p->suivant = tete;
 tete = p;
 }
 printf("Voici la liste construite\n");
 for (p = tete; p != NULL; p = p->suivant)
 printf("%d ", p->valeur);
  printf("\n"); }




Insertion en queue de liste
 NODE *tete = NULL, *queue; ...
 p = malloc(sizeof(NODE));
 if (p == NULL) {
 printf("Problème d'allocation de mémoire\n"); exit(-1); }
 p->valeur = x;
 p->suivant = NULL;
  if (tete == NULL)
   tete = p;
 else
 queue->suivant = p;
 queue = p;




                                                             12
Parcours d’une liste
 fonction trouver( L: Liste, x: entier): entier
 Var
 p : pointeur
 r : entier
 début
     p L
     r 0
     tant que (p != nil et p^.valeur !=x) faire
           p p^.suiv
           r r+1
      fintantque
    si (p^.valeur =x) alors
           Trouver r
     sinon
           Trouver 0
     finsi
  fin




Suppression dans une liste

    tête                        12           4    9
                  7

                  .             .           .
    tête

                  .             .            .




                                                      13
Suppression dans une liste
 Procédure supprimer(L: Liste, x: entier)
 Var
 p , prec: Liste
 début
         p L
         tant que (p != nil et p^.valeur !=x) faire
                  prec p
                  p p^.suiv
          fintantque
         si (p^.valeur = x) alors
         prec^.suiv p^.suiv
         liberer(p)

 fin




Autres formes de liste

   Liste bidirectionnelle (ou doublement chaînée)
  Définition
       En C                          En Algo
  struct noeud
  { int valeur;                      type Liste = pointeur sur noeud
                                     noeud =enregistrement
     struct noeud *suiv;
                                     valeur : entier
     sruct nœud *prec                suiv : pointeur sur nœud
  };                                 prec : pointeur sur noeud
   struct noeud * p;                 Fin




                                                                       14
Ajout d’un élément (liste bidirectionnelle)
Procedure ajout_tete(E/S L:Liste, x: entier)
var
p: Liste
Début
     p    nouveau (enregistrement nœud)
     p^.valeur     x
    si L = nil alors
          p^.prec    nil
          p^.suiv    nil
    sinon
          L^.prec p
          P^.suiv    L
          P^.prec nil
   finsi
     L p
fin




Ajout d’un élément (liste bidirectionnelle)
Procedure ajout_queue(E/S L:Liste, x: entier)
var
P, q: Liste
Début
   p     nouveau (enregistrement nœud)
                                                      q^.suiv   p
   p^.valeur      x
                                                      p^.prec   q
    si (L=nil) alors                                  p^.suiv   nil
           p^.prec    nil                       finsi
           p^.suiv    nil                   fin
           L    p
    sinon
          q=L
           tant que (q^.suiv != nil) faire
                    q q^.suiv




                                                                      15
Ajout d’un élément (liste bidirectionnelle)

  typedef struct
  {struct noeud * T /*adresse de la tête*/
   struct noeud * Q /*adresse de la queue*/
  } ListeD

  type ListeD = enregistrement
  T : pointeur sur nœud
  Q : pointeur sur nœud
  fin




Ajout d’un élément (liste bidirectionnelle)
  Procedure ajout(var L:ListeD, x: entier) //Ajout en queue de liste
  var
  p: ^noeud
  Début
      p    nouveau (enregistrement nœud)
      p^.valeur     x
     si (L^.Q = nil ) et (L^.T = nil ) alors
            p^.prec      nil         // la liste contiendra un seul element
            p^.suiv     nil          // la tete et la queue auront la même adresse
             L^.Q p
             L^.T p
       sinon
             p^.prec      L^.Q
             L^.Q^.suiv p
             P^.suiv nil
             L^.Q p
        finsi
  fin




                                                                                     16
Suppression (liste bidirectionnelle)

  Procédure supprimer(var L: ListeD, x: entier)
  Var
  p : ^noeud
  début
          p L^.T
          tant que (p != nil et p^.valeur !=x) faire
                   p p^.suiv
           fintantque
          si (p^.valeur = x) alors
          p^.suiv^.prec p^.prec
          p^.prec^.suiv      p^.suiv
          liberer(p)

  fin




Liste circulaire
     Listes simplement ou doublement chaînées
     La queue est reliée à la tête
     Pour la version doublement chaînée la tête est
     également reliée à la queue


    tête
                  E1           E2           E3         E4
                  .             .            .         .




                                                            17
Les piles : définition

   Correspond à la notion usuelle de pile (pile
   d’assiettes, pile de dossiers, etc..)
   Caractéristique principale : on retire d’abord le
   dernier élément déposé LIFO Last In First Out
   Utilisée fréquemment en informatique (appels
   en cascade des sous programmes le dernier
   appelé est exécuté en premier)
                              retrait
         Ajout
                    Sommet




Implémentation (tableau)

   La réalisation des opérations sur les
   piles peut s'effectuer en utilisant un
   tableau qui contient les éléments et un
   indice qui indiquera la position du
   sommet de la pile.




                                                       18
Opérations sur les piles
      En algo                          En C
Pile = enregistrement
T: tableau d'éléments               Typedef struct Pile
 taille: entier.                    { int    taille ;
Fin                                 Type Element contenu[MaxP];
fonction vide(P: pile ) :booléen    } Pile;
début
    Si P.taille = 0 alors
              vide    vrai
    Sinon vide faux
    Finsi
fin
procédure viderpile(var P: pile )
début
 P.taille 0
fin




Opérations sur les piles
fonction sommet( P: pile ) : Type Element
debut
    si taille != 0 alors
    sommet        P.T[ taille ]
    finsi
fin                               procédure empiler( x: élément
procédure dépiler( var P: pile)   var P: pile)
debut                             début
si (vide(p)= vrai) alors                  P.taille    P.taille +1
     ecrire ("erreur")                    P.T[ P.taille ]    x
Sinon                             fin
P.taille      P.taille -1
Finsi
fin




                                                                    19
Implémentation (liste chaînée)
Type
Cellule= enregistrement
 info : type de l’info
 suiv : pointeur sur pile
  Fin
Pile = ^Cellule
Procédure Initialiser(var P:Pile)
Début
P nil
Fin
 fonction Pile_vide(Sommet : Pile): booléen
   Debut
 Pile_vide    (Sommet = nil )
Fin




Procédure dépiler (var x: type info, var sommet: Pile)
var
Q: Pile
debut
si NON (Pile_vide (sommet)) alors
    x sommet^.info
   Q     sommet
   sommet        sommet^.suiv
    libérer( Q )
sinon
   ecrire ("impossible la pile est vide")
Finsi
Fin




                                                         20
Implémentation (liste chaînée)
 procédure empiler (x:type de l’info, var sommet: Pile)
 Var
 Q: Pile
 debut
 Q    nouveau (Pile) /* allouer( Q ) */
 Q^.info   x
 Q^.suiv   sommet
 sommet      Q
 Fin




Les files
   La file est une structure de données dynamique qui
   modélise les files d’attente (clients devant un guichet)
   utilisées en programmation pour gérer des objets qui
   sont en attente d'un traitement ultérieur, par exemple
   des processus en attente d'une ressource du système,
   Dans une file les éléments sont systématiquement
   ajoutés en queue et supprimés en tête, la valeur d'une
   file est par convention celle de l'élément de tête.
   stratégie FIFO First In First Out




                                                              21
Implémentation des files
    Implémentation :
  - Implémentation contiguë dans un tableau
  - Implémentation par liste chaînée



   tête                                              queue

               .          .            .




 Type                         type
 cellule=enregistrement       file= enregistrement
                              tete : ^cellule
 valeur : entier
                              Queue: ^cellule
 suiv : ^cellule              fin
 Fin

 Procédure Initialiser(Var F: file)
 Debut
   F.Tete   Nil
   F.Queue Nil
 Fin




                                                             22
Opérations sur les files
 procédure défiler(var X: entier , var F: file )
 var
 Q : ^cellule
 Debut
    si (F.tete ≠ Nil) alors
          Q F.tete
          X F.tete^.val
          F.tete    (F.tete) ^.suiv
          libérer (Q)
    Sinon
          Ecrire("Impossible, la file est vide")
    Finsi
 Fin




Opérations sur les files
 procédure enfiler( x: entier,var F: file )
 var
 Q: ^cellule
 début
    Q    nouveau(enregistrement cellule)
    Q^.val     x
    Q^.suiv Nil
    si (F.Queue ≠ Nil) alors
         (F.queue ) ^.suiv    Q
    sinon
          F.tete   Q
    Finsi
    F.queue      Q
    Fin




                                                   23

				
DOCUMENT INFO
Shared By:
Tags:
Stats:
views:9
posted:10/9/2012
language:
pages:23