Isup 1 - Programmation TD no4 Matthieu Journault 19 juin 2020
Exercice 1 : Rappel
Q. 1Ecrire une fonction r´´ ecursive qui calcule la mise `a la puissance n d’un entier m.
Q. 2Ecrire une fonction r´´ ecursive calculant le binomial
n
m
Q. 3Repr´esenter l’´etat de la m´emoire `a chacun des points de programme suivant (1, 2, . . .)
void swap(int a, int b) { /* 4 */
int c = b ; /* 5 */
b = a ; /* 6 */
a = c ; /* 7 */
}
void incr(int u) { /* 9 */
u = u + 1; /* 10 */
}
int main() { /* 1 */
int u = 0 ; /* 2 */
int v = 3 ; /* 3 */
swap(u, v); /* 8 */
incr(v); /* 11 */
incr(v); /* 12 */
}
Exercice 2 : Pointeurs
Q. 4Que remarquez vous concernant les modifications m´emoires dans la question 3 ? Comment expliquez vous ce ph´enom`ene ? Voyez vous un moyen permettant `a une fonction f appel´ee par une fonction g de modifier les variables locales deg.
Nous avons vu jusqu’`a maintenant plusieurs types de valeurs en C : les entiers, les flottants, les caract`eres. Il existe de nombreux autres types de valeurs en C, celui qui nous int´eresse particuli`erement aujourd’hui est le type depointeur.
La m´emoire est un grand bloc de “cases” telle que chaque case contient une valeur. Ces cases ont un emplacement dans la m´emoire, cet emplacement s’appellel’adresse de la case. L’adresse n’est en fait rien d’autre qu’un entier et la m´emoire peut ˆetre vu comme un tr`es grand tableau.
1
6 0
2 1
1 2
. . . 3
1498
2 1499
1 1500
7 1501
6 1502
valeur
adresse . . .
En C il est possible de modifier le contenu de n’importe quelle case m´emoire, `a condition bien sˆur, d’en connaˆıtre l’adresse. Il sera donc souvent n´ecessaire en C de manipuler des adresses des cases de la m´emoire. Toutefois afin de ne pas introduire de confusions pour le programmeur, le langage C s´epare les entiers repr´esentant des entiers, des entiers repr´esentant des adresses. Ce dernier type de valeur est appelle un pointeur. Ainsi une valeur de type pointeur n’est rien d’autre que l’adresse d’une case m´emoire. Les pointeurs peuvent “pointer” vers diff´erents types de valeurs (le type de la valeur contenue dans la case point´ee par le pointeur), ainsi diff´erents types de pointeur existent et le type d’un pointeur pointant vers une valeur de type t est t *. Par exemple un pointeur vers une case m´emoire contenant un entier a le type int *, un pointeur vers une case m´emoire contenant un caract`ere a le type char
*. De la mˆeme mani`ere qu’il est possible de stocker un entier dans une case m´emoire il est possible de stocker un pointeur dans une case m´emoire (rappel : ce pointeur n’est en fait rien d’autre qu’un entier).
Q. 5 Quel est alors le type d’un pointeur pointant vers une case m´emoire contenant un pointeur vers un entier.
Voyons maintenant comment manipuler un pointeur. Si e est une expression de type pointeur (par exemple une variable contenant un pointeur), alors l’expression * e permet de lire (ou de modifier si `a gauche d’une affectation) le contenu de la case point´ee pare. Six est un ´el´ement ayant une adresse dans la m´emoire (par exemple une variable) alors &x est un pointeur vers cette adresse dans la m´emoire.
Q. 6Supposons que la m´emoire soit dans l’´etat suivant : p : @x
y : 4 f
x : 1
main (La fl`eche de p vers x repr´esente le fait que la valeur stock´ee dans la variable p est un pointeur vers la variable x). Repr´esenter la m´emoire apr`es l’ex´ecution de chacune des 3 instructions suivantes `a partir de cette ´etat.
1. *p = 3;
2. y = *p + 5;
3. *p = *p + 1;
4. p = &y; *p = 3;
5. *p = *(&y);
Q. 7Pour chacun des programme suivant donner les diff´erents ´etats de la m´emoire :
1. int x = 1; /* 1 */
int* y = &x; /* 2 */
int z = *y; /* 3 */
2
2. int* x; /* 1 */
x = &x; /* 2 */
int y = *x; /* 3 */
3. int x = 1; /* 1 */
int* y = &x; /* 2 */
int** z = &y; /* 3 */
int w = **z; /* 4 */
4. int* x; /* 1 */
x = &x; /* 2 */
int y = **** ((int****) x); /* 3 */
Le fait qu’une fonction modifie l’´etat de la m´emoire autre que ses propres variables locales est appel´ee uneffet de bord.
Q. 8 Ecrire une fonction´ incr dont vous pr´eciserez la signature et qui a pour effet d’incr´ementer la valeur stock´ee dans une variable. Donner un exemple d’utilisation d’une telle fonction, et repr´esenter les diff´erents ´etats de la m´emoire au long de l’ex´ecution de votre programme.
Q. 9D´efinir une fonctionswappermettant l’inversion du contenu de deux variables. Donner un exemple d’utilisation d’une telle fonction, et repr´esenter les diff´erents ´etats de la m´emoire au long de l’ex´ecution de votre programme.
Q. 10 On consid`ere la transformation lin´eaire f :
x
y
7→
x+y
x−y
. D´efinir une fonction f, dont vous pr´eciserez la signature, et appliquant cette transformation par effet de bord `a ses deux arguments. De mˆeme pour la fonction inverse de f.
Exercice 3 : Tri de 3 valeurs
Q. 11 D´efinir une fonction void tri2(int* x, int* y), qui place le minimum des valeurs point´ees par int* x et int* y dans la case m´emoire point´ee par int* x et le maximum dans la case m´emoire point´ee parint* y. Utiliser la fonction swap d´efinie pr´ec´edemment.
Q. 12D´efinir une fonctionvoid tri3(int* x, int* y, int* z), qui place la plus petite valeur parmis les trois valeurs point´ees par int* x, int* y et int* z dans la case m´emoire point´ee par int* x, la deuxi`eme dans la case m´emoire point´ee par int* y et la plus grande dans celle point´ee par int* z.
Utiliser la fonction tri2.
Q. 13 Tester votre programme en appelant tri3 sur des pointeurs pointant sur des cases m´emoires contenant 3, 2 et1 (dans cet ordre). Repr´esenter la m´emoire en entr´ee et en sortie de chaque fonction.
Exercice 4 : Pointeurs et pile d’appel
3
Q. 14Expliquer ce qui se passe lors de l’ex´ecution du programme suivant :
int* f1(int* x, int* y) { if (*x < *y) {return x;}
else {return y;}
}
int* f2(int* x, int* y) { int c;
if (*x < *y) {c = *x;}
else {c = *y;}
return (&c);
}
int main() { int x = 1;
int y = 2;
int* p = f1(&x, &y);
int* q = f2(&x, &y);
}
4