You are on page 1of 7

Programmation C

Structures dynamiques
David Pointcheval

Plan

1 - Structures dynamiques 2 - Exemple : listes chaînées 3 - Application : piles

E-mail : David.Pointcheval@ens.fr Web : http://www.di.ens.fr/~pointche/enseignement/ensta

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 2

Liste chaînée

Liste chaînée (suite)
struct s_cellule { int value; struct s_cellule *next; }; typedef struct s_cellule cellule; typedef cellule *list;

La liste chaînée peut donc s’implémenter de la façon suivante :
struct s_cellule { int value; struct s_cellule *next; };
David Pointcheval LIENS - CNRS

struct s_cellule = cellule list = pointeur sur une cellule Liste vide = pointeur particulier ⇒ NULL
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 4

typedef struct s_cellule cellule; typedef cellule *list;

Programmation C - ENSTA - 3

Liste chaînée (suite)
struct s_cellule { int value; struct s_cellule *next; };

Création d’une cellule
L
list L; /* pointeur sur une cellule mais pas de cellule associée */ L = malloc(sizeof(cellule)); /* mémoire allouée cellule non initialisée */

Type cellule

? L

typedef struct s_cellule cellule; typedef cellule *list; list nil = NULL;

Type list

Liste vide = nil NULL = pointeur 0
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 5

*L est une cellule : (*L).value : champ value (*L).next : champ next
David Pointcheval LIENS - CNRS

L->value L->next
Programmation C - ENSTA - 6

Création d’une liste chaînée
list cons(int x, list L) { list L1; L1 = malloc(sizeof(cellule)); L1->value = x; L1->next = L; return L1; }

Opérations sur les listes
Ajout d’un élément Tête et Queue
int head(list L) { return L->value; } list tail(list L) { return L->next } list cons(int x, list L) { list L1; L1 = malloc(sizeof(cellule)); L1->value = x; L1->next = L; return L1; }

L …

Soit une liste L Soit un entier x
David Pointcheval LIENS - CNRS

x L1 L1 nouvelle liste
Programmation C - ENSTA - 7

1 L1
David Pointcheval LIENS - CNRS

6
tail(L1)

L

head(L1)

Programmation C - ENSTA - 8

Fonctions récursives : concat
2 L1 5 3 L1 2

Concat (suite)
5 L2
LL = concat(→ 2, L2) L = concat(nil, L2) ⇒ L2 L = L2 ⇒ cons(2,L2) = → 2 → L2 LL = → 2 → L2
Programmation C - ENSTA - 10

3

L2 La concaténation de deux listes L1 et L2 : ajouter la tête de L1 à la concaténation de la queue de L1, et L2 Rq : si L1 est vide, retourner L2
list concat(list L1, list L2) { list L; if (L1 == nil) return L2; L = concat(tail(L1),L2); return cons(head(L1),L); }

list concat(list L1, list L2) { list L; if (L1 == nil) return L2; L = concat(tail(L1),L2); return cons(head(L1),L); }

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 9

David Pointcheval LIENS - CNRS

Concat (suite)
5 L2 2 L1
list concat(list L1, list L2) { list L; if (L1 == nil) return L2; L = concat(tail(L1),L2); return cons(head(L1),L); }
Programmation C - ENSTA - 11

Fonction concat - II
2 L1 L2 5 3

3

2 LL
David Pointcheval LIENS - CNRS

La modification de la fin de LL affectera L2

La concaténation de deux listes L1 et L2 : ajouter la tête de L1 à la concaténation de la queue de L1 et L2 Rq : si L1 est vide, retourner une copie de L2
list concat(list L1, list L2) { if (L1 == nil) return copie(L2); return cons(head(L1), concat(tail(L1),L2)); }
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 12

Listes chaînées - fin
Ainsi, la structure récursive des listes permet une programmation très aisée • ajout d’un élément • copie, affichage, concaténation • recherche d’un élément • suppression d’un élément •…
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 13

Listes chaînées et piles
Une pile est un objet simple qui n’accepte que deux actions : • push - empiler un objet en tête = cons des listes chainées • pop - retourner, et supprimer, l’objet de tête = head et tail des listes chaînées (à la fois)
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 14

Structure de pile

Pile : push
Ajout d’un élément en tête = cons
pile push(int x, pile P) { pile P1; P1 = malloc(sizeof(cellule)); P1->value = x; P1->next = P; return P1; }

Une pile est une liste chaînée :
struct s_cellule { int value; struct s_cellule *next; };
David Pointcheval LIENS - CNRS

typedef struct s_cellule cellule; typedef cellule *pile;

1 P1 P

6

Programmation C - ENSTA - 15

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 16

Pile : push (suite)
1 P1 P 6 …

Pile : push (suite)
pile push(int x, pile P) { pile P1 = malloc(sizeof(cellule)); P1->value = x; P1->next = P; void push(int x, pile P) { return P1; pile P1 = malloc(sizeof(cellule)); } P1->value = x;

L’utilisation Q=push(x,P) permet de conserver le pointeur P sur l’ancienne pile ! Pour éviter cette « mauvaise » utilisation, on peut imposer la modification de la pile passée en argument
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 17

P1->next = P; P = P1; }

P est la pile à modifier push(1, P); ajoute une cellule avec 1 en tête… Passage par adresse
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 18

Pile : push (suite)
pile *PP : Passage par adresse d’une pile
void push(int x, pile P) { pile P1 = malloc(sizeof(cellule)); P1->value = x; void push(int x, pile *PP) { P1->next = P; pile P1 = malloc(sizeof(cellule)); P = P1; } P1->value = x;

Pile : push (suite)
void push(int x, pile *PP) { pile P1 = malloc(sizeof(cellule)); P1->value = x; P1->next = *PP; *PP = P1; }

P1->next = *PP; *PP = P1; }

push(1, &P);

*PP ( = P ) est la pile à modifier ⇒ PP est un pointeur sur cette pile push(1, &P); On ne peut pas modifier PP, le pointeur &P sur P mais on peut modifier *PP, la pile *(&P) = P
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 19

0x0800 PP0x080C value 0x0900 1 0x0A00 P10x0902 0x0B00 0x0C00
value value

P next

0x0B03

0x0B03
next 0x0C02

3

2

next 0x0000

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 20

Pile : push (suite)
void push(int x, pile *PP) { pile P1 = malloc(sizeof(cellule)); P1->value = x; P1->next = *PP; *PP = P1; }

Pile : push (suite)
void push(int x, pile *PP) { pile P1 = malloc(sizeof(cellule)); P1->value = x; P1->next = *PP; *PP = P1; }

push(1, &P);

0x0800 PP0x080C value 0x0900 1 0x0A00 P10x0902 0x0B00 0x0C00
value value

P next

0x0902

push(1, &P);

0x0B03
next 0x0C02

0x0800 0x0900 0x0A00 0x0B00 0x0C00

P value

0x0902

1 3

next

0x0B03
next 0x0C02

3

value value

2

next 0x0000

2

next 0x0000

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 21

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 22

Pile : pop
Extraction et Suppression de l’élément de tête
int pop(pile *PP) { int x = (*PP)->value; *PP = (*PP)->next; return x; }

Pile : pop (suite)
int pop(pile *PP) { int x = (*PP)->value; *PP = (*PP)->next; return x; } 0x0800 PP0x080C value 0x0900 1 0x0A00 x 0x0B00 0x0C00
P next

a = pop(&P);

0x0B03

Sur une pile P, on fait a = pop(&P); • retourne, dans a, la tête • modifie la pile P
David Pointcheval LIENS - CNRS Programmation C - ENSTA - 23 David Pointcheval LIENS - CNRS

0x0B03
next 0x0C02

1
value value

3

2

next 0x0000

Programmation C - ENSTA - 24

Pile : pop (suite)
int pop(pile *PP) { int x = (*PP)->value; *PP = (*PP)->next; return x; } 0x0800 PP0x080C value 0x0900 1 0x0A00 x 0x0B00 0x0C00
P next

Pile : pop (suite)
int pop(pile *PP) { int x = (*PP)->value; *PP = (*PP)->next; return x; }
P

a = pop(&P);

0x0B03

a = pop(&P);

0x0B03
next 0x0C02

0x0800 0x0900 0x0A00 x

0x0B03

1
value value

1
value value

3

0x0B00 0x0C00

3

next 0x0C02 next 0x0000 a

2

next 0x0000

2

1

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 25

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 26

Conclusion
Le passage par adresse permet de modifier les arguments, et donc • de « restreindre » l ’utilisation de certaines fonctions push modifie la pile • de « retourner » plusieurs résultats :
– pile modifiée – élément de tête

David Pointcheval LIENS - CNRS

Programmation C - ENSTA - 27