Académique Documents
Professionnel Documents
Culture Documents
Christophe Tollu
(Première version des diapos par Julien David)
24 janvier 2023
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 1 / 33
F`o“n`cˇtˇi`o“n¯s ˚r`é´cˇu˚r¯sfi˚i‹vfle˙s
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 2 / 33
Fonctions récursives
Définitions
Une fonction récursive est une fonction qui, dans sa définition, contient
un appel à elle-même.
Un appel récursif est un appel réalisé alors que l’exécution d’un appel
précédent de la même fonction n’est pas achevé.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 3 / 33
Deux exemples très classiques
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 4 / 33
Récursivité : terminaison
Empilement potentiellement infini des appels récursifs
1 int f ( int n)
2 { r e t u r n f ( n−1);}
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 5 / 33
Récursivité terminale / non terminale
Définitions
Un appel récursif est non terminal s’il est utilisé comme sous-expression
stricte d’une expression évaluée dans l’appel “parent” de la (même)
fonction. [cf. fonction somme premiers entiers]
Un appel récursif est terminal si son résultat est celui de l’appel “parent”
de la (même) fonction [cf. fonction pgcd] .
Une fonction récursive est (à récursivité) terminale si tous ses appels
récursifs sont terminaux.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 6 / 33
Récursivité terminale / non terminale
Optimisation possible
Dans le cas d’une récursivité terminale, il n’est (théoriquement) pas
nécessaire d’empiler les appels : l’appel récursif ”remplace”
simplement l’appel parent sur la pile (en passant les nouvelles valeurs
des arguments et en conservant l’adresse de retour de l’appel initial).
Le compilateur, dans certains contextes d’optimisation, peut donc traiter
une fonction récursive comme une fonction itérative et optimiser la
gestion de la pile (tail-call optimization).
L’option -O2 du compilateur gcc active ce niveau d’optimisation (sans
aucune garantie que les fonctions récursives qui peuvent être ainsi
”dérécursivées” le seront effectivement).
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 7 / 33
Factorielle non terminale et terminale
Factorielle terminale
1 unsigned f a c t 2 t e r ( unsigned n , unsigned acc )
2 {
3 i f ( n <= 0 )
4 r e t u r n acc ;
5 r e t u r n f a c t 2 t e r ( n−1, n∗acc ) ;
6 }
7
8 unsigned f a c t t e r ( unsigned n )
9 {
10 return fact2 ter (n , 1);
11 }
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 8 / 33
Récursivité et tableaux
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 9 / 33
Récursivité et efficacité
Pour exécuter l’appel f(20), on exécutera plus de trois mille trois cents appels
f(2) !
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 10 / 33
Récursivité et efficacité : mémoı̈sation
Fibonacci mémoı̈sé
1 # d e f i n e N 50
2 unsigned f i b t a b [N] = {0};
3
4 unsigned fib mem ( unsigned f i b t a b [ ] , unsigned n )
5 {
6 i f ( 0 == n | | 1 == n )
7 return ( fib tab [n] = n ) ;
8 i f ( 0 == f i b t a b [ n ] )
9 f i b t a b [ n ] = fib mem ( f i b t a b , n−1) + fib mem ( f i b t a b , n−2);
10 return fib tab [n ] ;
11 }
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 11 / 33
Récursivité et efficacité : récursivité terminale
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 12 / 33
Récursivité et efficacité
Récursif vs itératif
Il existe des techniques pour remédier à la redondance des appels dans
une fonction récursive.
Il ne faut donc pas croire que l’exécution d’une fonction récursive est
toujours nettement plus coûteuse que celle d’une fonction itérative
effectuant la même tâche !
En outre, l’écriture d’une fonction récursive est, dans de nombreuses
applications, beaucoup plus naturelle . . .
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 13 / 33
Récursivité et efficacité
TD 1 exercice 1 (version itérative Θ(n2 ))
1 i t e m m a x d i f f t a b ( i t e m t [ ] , i n t deb , i n t f i n ) {
2 int i , j ;
3 item res = 0;
4 f o r ( i = deb ; i <= f i n ; ++ i )
5 f o r ( j = i +1; j <= f i n ; ++ j )
6 i f ( t [ j ]− t [ i ] > r e s )
7 r e s = t [ j ]− t [ i ] ;
8 r e t u r n res ;
9 }
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 15 / 33
Zones de mémoire
Avertissement liminaire
La présentation de la mémoire qui suit est schématique. Elle ne vise
qu’à faciliter la compréhension des pointeurs (et plus tard de l’allocation
dynamique).
Pour plus de détails sur l’organisation de la mémoire, les modes
d’allocation, la correspondance adresses virtuelles – adresses
physiques, etc., il faudra attendre un cours sur l’architecture des
machines et les systèmes d’exploitation.
En première approximation...
la mémoire peut être représentée comme un (long) tableau d’octets
segmenté en plusieurs zones.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 16 / 33
Zones de mémoire
Pile d’appel Variables locales, paramètres d’entrée des fonctions, adresse de retour,
Espace “libre”
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 17 / 33
Zone texte et zone de données
Rappel
Un fichier exécutable contient déjà :
Une zone correspondant aux variables globales et statiques.
Une zone correspondant à l’ensemble des fonctions du programme.
Zone texte
Cette zone est en lecture seule.
Sa taille est déterminée avant l’exécution.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 18 / 33
Zone texte et zone de données
Rappel
Un fichier exécutable contient déjà :
Une zone correspondant aux variables globales et statiques.
Une zone correspondant à l’ensemble des fonctions du programme.
Zone texte
Cette zone est en lecture seule.
Sa taille est déterminée avant l’exécution.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 18 / 33
Zone texte et zone de données
Rappel
Un fichier exécutable contient déjà :
Une zone correspondant aux variables globales et statiques.
Une zone correspondant à l’ensemble des fonctions du programme.
Zone texte
Cette zone est en lecture seule.
Sa taille est déterminée avant l’exécution.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 18 / 33
Zone texte et zone de données
Zone de données
Cette zone peut être en lecture seule ou en lecture–écriture.
La taille de cette zone (et de chaque objet global ou statique) est
déterminée avant l’exécution..
Accessibilité
Les variables dans la zone de données et les fonctions dans la zone texte
sont accessibles depuis tout point du programme (ou depuis tout point
d’un segment correspondant à un fichier objet pour les objets statiques).
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 19 / 33
Zone texte et zone de données
Zone de données
Cette zone peut être en lecture seule ou en lecture–écriture.
La taille de cette zone (et de chaque objet global ou statique) est
déterminée avant l’exécution..
Accessibilité
Les variables dans la zone de données et les fonctions dans la zone texte
sont accessibles depuis tout point du programme (ou depuis tout point
d’un segment correspondant à un fichier objet pour les objets statiques).
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 19 / 33
Zone texte et zone de données
Zone de données
Cette zone peut être en lecture seule ou en lecture–écriture.
La taille de cette zone (et de chaque objet global ou statique) est
déterminée avant l’exécution..
Accessibilité
Les variables dans la zone de données et les fonctions dans la zone texte
sont accessibles depuis tout point du programme (ou depuis tout point
d’un segment correspondant à un fichier objet pour les objets statiques).
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 19 / 33
Pile d’appel
Caractérisation
La pile d’appel permet de :
stocker les variables locales,
stocker les paramètres d’entrée des fonctions,
sauvegarder l’adresse de retour, la taille du segment actif de la pile (ou le
frame pointeur), . . . .
Précision
Dans ce cours on représente les appels de fonctions avec une zone
grisée dans la pile.
Cette zone grisée contient, entre autres, les informations permettant, à la
fin de l’exécution d’une fonction, de dépiler correctement en réactivant
la zone (frame) de l’appel précédent.
Le nombre et la nature des informations sauvegardées dans cette zone,
donc sa taille, dépendent de l’architecture.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 20 / 33
Les limites de la pile d’appel
Limites
La taille de la pile
Les limites d’accès des variables locales
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 21 / 33
Les limites de la pile d’appel
La taille de la pile
La pile a une taille limitée.
À chaque appel de fonction, on ajoute des informations dans la pile.
Si un programme effectue trop d’appels imbriqués, la pile déborde.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 22 / 33
Les limites de la pile d’appel
Exemple
Essayons d’écrire une fonction qui échange la valeur de deux variables.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 23 / 33
1 v o i d swap ( i n t x , i n t y ){
2 i n t tmp=x ;
3 x=y ;
4 y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap ( x , y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
y 7 972 y 5 972
x 5 976 x 7 976
y 7 996
swap(5,7) swap(5,7)
x 5 100
y 7 996 y 7 996
main
x 5 1000 x 5 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 24 / 33
1 v o i d swap ( i n t x , i n t y ){
2 i n t tmp=x ;
3 x=y ;
4 y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap ( x , y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
y 7 972 y 5 972
x 5 976 x 7 976
y 7 996
swap(5,7) swap(5,7)
x 5 100
y 7 996 y 7 996
main
x 5 1000 x 5 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 24 / 33
1 v o i d swap ( i n t x , i n t y ){
2 i n t tmp=x ;
3 x=y ;
4 y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap ( x , y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
y 7 972 y 5 972
x 5 976 x 7 976
y 7 996
swap(5,7) swap(5,7)
x 5 100
y 7 996 y 7 996
main
x 5 1000 x 5 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 24 / 33
1 v o i d swap ( i n t x , i n t y ){
2 i n t tmp=x ;
3 x=y ;
4 y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap ( x , y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
y 7 972 y 5 972
x 5 976 x 7 976
y 7 996
swap(5,7) swap(5,7)
x 5 100
y 7 996 y 7 996
main
x 5 1000 x 5 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 24 / 33
Constat
Notre accès à la mémoire est pour l’instant très limité.
Une fonction ne peut accéder qu’à ses propres variables locales et
paramètres formels ou à des variables de la zone de données.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 25 / 33
P̀o˘i‹n˚t´eˇu˚r¯s `eˇt `a`d˚r`e˙sfi¯sfi`e˙s
”m`é›m`o˘i˚r`e
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 26 / 33
Variables de type pointeur
Rappel
Une variable est caractérisée par :
son nom
son type
sa valeur
son adresse
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 27 / 33
Variables de type pointeur
À quoi bon ?
En stockant l’adresse d’autres variables, on va pouvoir accéder à n’importe
quelle zone de la mémoire.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 28 / 33
Variables de type pointeur
À quoi bon ?
En stockant l’adresse d’autres variables, on va pouvoir accéder à n’importe
quelle zone de la mémoire.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 28 / 33
Variables de type pointeur
À quoi bon ?
En stockant l’adresse d’autres variables, on va pouvoir accéder à n’importe
quelle zone de la mémoire.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 28 / 33
Variables de type pointeur
Déclaration
int * p;
Nom : p
Type : pointeur sur un entier (int *)
char * c;
Nom : c
Type : pointeur sur un caractère (char *)
Important ! Usage
Soit la déclaration int * z;
int* désigne le type de la variable z, à savoir pointeur vers int
*z désigne l’objet pointé par z (déréférencement de z)
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 29 / 33
Variables de type pointeur
1 i n t main ( ) {
2 i n t z =6; p 1000 996
3 i n t ∗ p=&z ;
4 p r i n t f ( ”%d\n ” ,∗ p ) ;
5 r e t u r n EXIT SUCCESS ; z 6 1000
6 }
main
Le programme affiche 6
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 30 / 33
Initialisation d’un pointeur
Initialisation
Comme toute variable, une variable de type pointeur doit être initialisée.
Si un pointeur n’est pas initialisé, il peut contenir une adresse
quelconque et modifier par erreur la case mémoire correspondante peut
avoir de graves conséquences.
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 31 / 33
Pointeurs
Échange
On peut à présent réécrire la fonction swap
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 32 / 33
1 v o i d swap ( i n t ∗ x , i n t ∗ y ){
2 i n t tmp=∗x ;
3 ∗x=∗y ;
4 ∗y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap(& x ,& y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
swap(1000,996) swap(1000,996)
x 7 1000
y 7 996 y 5 996
main
x 5 1000 x 7 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 33 / 33
1 v o i d swap ( i n t ∗ x , i n t ∗ y ){
2 i n t tmp=∗x ;
3 ∗x=∗y ;
4 ∗y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap(& x ,& y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
swap(1000,996) swap(1000,996)
x 7 1000
y 7 996 y 5 996
main
x 5 1000 x 7 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 33 / 33
1 v o i d swap ( i n t ∗ x , i n t ∗ y ){
2 i n t tmp=∗x ;
3 ∗x=∗y ;
4 ∗y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap(& x ,& y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
swap(1000,996) swap(1000,996)
x 7 1000
y 7 996 y 5 996
main
x 5 1000 x 7 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 33 / 33
1 v o i d swap ( i n t ∗ x , i n t ∗ y ){
2 i n t tmp=∗x ;
3 ∗x=∗y ;
4 ∗y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap(& x ,& y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
swap(1000,996) swap(1000,996)
x 7 1000
y 7 996 y 5 996
main
x 5 1000 x 7 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 33 / 33
1 v o i d swap ( i n t ∗ x , i n t ∗ y ){
2 i n t tmp=∗x ;
3 ∗x=∗y ;
4 ∗y=tmp ; y 7 996
5 }
6 i n t main ( ) {
7 i n t x =5; x 5 1000
8 i n t y =7;
9 swap(& x ,& y ) ; main
10 r e t u r n EXIT SUCCESS ;
11 }
swap(1000,996) swap(1000,996)
x 7 1000
y 7 996 y 5 996
main
x 5 1000 x 7 1000
main main
Christophe Tollu (Première version des diapos par Julien David) (A209 (poste 3691) - ct@lipn.univ-paris13.fr) 24 janvier 2023 33 / 33