Sunteți pe pagina 1din 8

Cours 2

Passage par adresse

2.1 Introduction
En C++, lorsque l’on modifie un paramètre dans une fonction, si l’on souhaite que les modi-
fications soient effectives dans le reste du programme, il faut passer le paramètre par référence.
� �
Ainsi, l’exécution du code :
1 void change(int & nb)
2 {
3 cout << "nb = " << nb << endl;
4 nb = 3;
5 cout << "nb = " << nb << endl;
6 }
7

8 int main()
9 {
10 int i = 5;
11 cout << "i = " << i << endl;
12 change(i);
13 cout << "i = " << i << endl;
14 return 0;

� �
15 }

� �
affichera :
1 i = 5
2 nb = 5
3 nb = 3

� �
4 i = 3

Malheureusement, le passage par référence n’existe pas en C. Pour que la modification d’une
variable passée en paramètre se répercute dans le programme, il faut alors passer à la fonction
l’adresse de cette variable. Le type du paramètre n’est pas du même type que la variable (ex :
float) mais un pointeur (pointant sur des valeurs de même type que la variable que l’on souhaite
modifier, par exemple float *).
Ainsi, si v est une variable (de type int) que l’on veut faire modifier par une fonction f, on
passe à f comme valeur d’argument l’adresse &v de v. On doit donc déclarer que le paramètre
� �
correspondant de f est un pointeur :

� �
1 void f(int *p) { ... *p ...}

Cette fonction est appelée f(&v). Le passage de l’argument &v se fait par copie, ce qui fait
que l’adresse de v est copiée dans p. Dans le corps de la fonction, à chaque fois que *p est lue ou

17
IUT de Villetaneuse

écrite, c’est donc la variable d’adresse &v qui est lue ou écrite, c’est-à-dire v. Ce mode de passage
des arguments est appelé passage par adresse.

Exemple 1
Modifions la fonction change définie ci-dessus de manière à transmettre le paramètre par
� �
adresse :
1 #include "iutv.h"
2 void change(int *p)
3 {
4 cout << "*p = " << *p << endl;
5 *p = 3;
6 cout << "*p = " << *p << endl;
7 }
8

9 int main()
10 {
11 int i = 5;
12 cout << "i = " << i << endl;
13 change(&i);
14 cout << "i = " << i << endl;
15 return 0;

� �
16 }

i p
i (main) (change)
(main) 5
5
(a) Après exécution de la ligne 11
(b) Avant exécution de la ligne 5

i p
(main) (change) i
3 (main)
3
(d) Après exécution de la ligne 13
(c) Après exécution de la ligne 5

Figure 2.1 – Passage d’un paramètre par adresse

Observons pas à pas ce qui se passe lorsque le code de l’exemple est exécuté :
1. La ligne 11 déclare une une variable entière nommée i, et l’initialise avec la valeur 5.
2. Lors de l’appel de la fonction change, on passe en paramètre l’adresse de la variable i.
3. Lors de l’exécution du corps de la fonction change, p contient l’adresse de la variable i.
4. L’instruction de la ligne 4 affiche la valeur de i, à savoir 5.
5. La ligne 5 modifie le contenu de la case mémoire pontée par p. Comme le pointeur p contient
l’adresse de la variable i, cela modifie alors la variable i pour mettre 3.
6. L’instruction 6 affiche le contenu *p de la case mémoire pointée par p, soit 3.
7. A la fin d’appel de la fonction, la variable p est détruite et l’état de la mémoire est celui
indiqué dans la figure 2.1(d).

� �
Langage C �18 � 1ère année (2013/2014)
Département informatique

Exemple 2
Écrivons maintenant une fonction echange, échangeant les valeurs de deux variables entières,
en utilisant les pointeurs :
� �
1 #include "iutv.h"
2

3 void echange(int *x, int *y)


4 {
5 int temp = *x;
6 *x = *y;
7 *y = temp;
8 }
9

10 int main()
11 {
12 int a=1;
13 int b=2;
14

15 echange(&a, &b);
16 cout << a << "\t" << b << endl;
17 return 0;

� �
18 }

Remarque : Le passage par adresse en C est plus lourd que le passage par référence en C++,
mais il a l’avantage d’être explicite à l’appel de la fonction : quand on voit un appel echange(&a,
&b), il est clair qu’il s’agit d’un appel par adresse, tandis qu’en C++, devant un appel echange(a,
b), il faut aller voir la déclaration de la fonction pour savoir s’il s’agit d’un appel par référence ou
d’un appel par copie.

2.2 Passage de types structurés par adresse


2.2.1 Pointeurs de types structurés
Comme pour les types simples, il est possible de déclarer des pointeurs sur des types structurés.
Pour déclarer un pointeur sur un type structuré, il faut ajouter le symbole * avant le nom de la
� �
variable. À titre d’exemple, supposons que le type struct etudiant soit défini par :
1 struct etudiant
2 {
3 string nom;
4 string prenom;

� �
5 };

� �
L’instruction :

� �
1 struct etudiant *pp;

déclare une variable de nom pp qui est un pointeur sur le type structuré struct etudiant. Ce
� �
pointeur peut alors contenir l’adresse d’une variable de type struct etudiant :
1 struct etudiant e1 = {"Dupond","Martin"};

� �
2 pp = &e1;

� �
On peut alors accéder aux champs de la variable e1 à l’aide du pointeur pp :

� �
1 cout<< "Nom: " << (*pp).nom << ", prénom: "<<(*pp).prenom << endl;

� �
1ère année (2013/2014) �19 � Langage C
IUT de Villetaneuse

Attention : L’opérateur . étant prioritaire sur l’indirection *, les parenthèses sont obligatoires.
Mais il y a une autre notation. Au lieu de (*pp).nom, on utilise l’opérateur -> pour écrire plus
� �
simplement :

� �
1 cout<< "Nom: " << pp->nom << ", prénom: "<<pp->prenom << endl;

La syntaxe pointeur->champ est équivalente à (*pointeur).champ.


Une variable de type pointeur vers un type structuré peut aussi subir de l’arithmétique comme
� �
les autres pointeurs. L’incrémenter par exemple, la fait pointer vers le type structuré suivant 1 :
1 struct etudiant s1groupeB2[27];
2 struct etudiant *pp;
3

4 /* initialisation du tableau d’étudiant(s) */


5 ......
6

7 pp = &s1groupeB2[20]; /* pp reçoit l’adresse du 21ième étudiant */


8

9 cout<< "Nom :" << pp->nom << endl; /* Affiche le nom du 21e étudiant */
10 pp=pp+1; /* pp pointe le 22ième étudiant */
11

12 cout<< "Nom :" << pp->nom << endl; /* Affiche le nom du 22e étudiant */
13

14 pp = s1groupeB2; /* pp reçoit l’adresse de la première case du tableau étudiant*/


15 pp++; /* pp pointe sur le 2e étudiant */
16

17 cout<< "Nom :" << pp->nom << endl; /* Affiche le nom du 2ème étudiant */
18

19 pp = pp + 2; /* pp reçoit l’adresse du 4ième étudiant */


20

� �
21 cout<< "Nom :" << pp->nom << endl; /* Affiche le nom du 4ième étudiant */

2.2.2 Passage par adresse


Si l’on souhaite modifier une variable de type structuré passée en paramètre d’une fonction,
il faut, comme dans le cas des types simples, passer cette variable par adresse. Le paramètre est
� �
donc alors de type pointeur sur le type structuré. Voici un exemple :
1 struct point
2 {
3 int x;
4 int y;
5 };
6

7 /* p est un pointeur sur le type structuré struct point.


8 dx et dy sont transmis par valeur */
9 void deplacerPoint (struct point* p, int dx, int dy)
10 {
11 p->x += dx;
12 p->y += dy;
13 }
14

15 int main ()
16 {
17 struct point p1 = {3,8};

1. En fait, l’incrémentation décale le pointeur du nombre d’octets égal à l’occupation mémoire du type structuré
pointé.

� �
Langage C �20 � 1ère année (2013/2014)
Département informatique

18 deplX = 4;
19 deplY = 9;
20 deplacerPoint (&p1, deplX, deplY);
21 return 0;

� �
22 }

Observations :
1. Le programme appelant passe trois paramètres à la fonction deplacerPoint() :
– &p1, c’est-à-dire l’adresse de la variable structurée p1, dans l’exemple cette adresse vaut :
4500,
– deplX et deplY, deux entiers transmis par valeur qui valent respectivement 4 et 9.
2. p->x permet d’accéder au champ x de la variable structurée du programme appelant, c’est-
à-dire la variable p1 dont l’adresse est 4500.
Ainsi p->x += dx; ajoute deplX au champ x de la variable p1 du programme appelant,
Ainsi p->y += dy; ajoute deplY au champ y de la variable p1 du programme appelant.
3. Finalement, la transmission par copie de l’adresse de la variable structurée p1 a permis à la
fonction de modifier indirectement la variable p1 du programme appelant.

en mémoire centrale

p1
4500
/3 7 /8 17

deplX deplY

4 9

Environnement Appelant

p dx dy
4567
4500 4 9

Environnement Appelé

Figure 2.2 – Passage par adresse

Remarque : Même si l’on ne modifie pas une variable structurée passée en paramètre, il peut
être intéressant de passer cette variable par adresse si le type structuré occupe beaucoup d’espace
mémoire. En effet, dans ce cas, plutôt que de copier les champs de la variable, le programme copie
uniquement l’adresse de cette variable. Cela permet d’accélérer l’exécution du programme.

� �
1ère année (2013/2014) �21 � Langage C
IUT de Villetaneuse

TD2 : Passage par adresse

Exercice 1 : Paramètres et pointeurs


� �
Soit la fontion suivante :
1 void faitQuoi(int x, int y, int d)
2 {
3 x = x + d;
4 y = y + d;

� �
5 }

� �
Soit le programme principal suivant
1 int main()
2 {
3 int a = 8;
4 int b = 2;
5 int c = 4;
6 faitQuoi (a, b, c);
7 cout << "a=" << a << " b=" << b << endl;
8

9 return 0;

� �
10 }

Question 1.1 : Dérouler le programme et "dessiner la mémoire" très précisément au fur et à


mesure de l’exécution du programme. Indiquer ce qui s’affiche.

� �
Question 1.2 : Même question lorsque la fonction est définie par
1 void faitQuoiBis(int *px, int *py, int d)
2 {
3 *px = *px + d;
4 *py = *py + d;

� �
5 }

� �
et la fonction main par
1 int main()
2 {
3 int a = 8;
4 int b = 2;
5 int c = 4;
6 faitQuoiBis (&a, &b, c);
7 cout << "a=" << a << " b=" << b<<endl;
8

9 return 0;

� �
10 }

Exercice 2 : Factorielle
Écrire une fonction factorielle void factorielle(int n, int * reponse) qui calcule la fac-
torielle d’un entier et écrire un programme de test qui l’utilise.

Exercice 3 : Échange
� �
Langage C �22 � 1ère année (2013/2014)
Département informatique

1. Écrire une fonction C qui échange deux nombres réels (double). Tester un programme
principal appelant cette fonction.

Exercice 4 : Le minimim, maximum, médiane


Écrire une fonction minMax3nb permettant de déterminer le minimum et le maximum de 3
nombres passés en paramètre.

Exercice 5 : Manipulation de l’heure


Question 5.1 : Définir une structure heure comprenant 3 champs de type entier appelés h, min
et sec correspondant aux heure, minute et seconde.
Question 5.2 : Définir la fonction void ajouteUneSeconde(...) ajoutant une seconde à une
heure passée en paramètre.
Question 5.3 : Définir la fonction afficheHeure prenant en paramètre une heure et l’affichant
sous la forme heure:minute:seconde.
Question 5.4 : Écrire un programme principal déclarant une variable de type struct heure
initialisée à 0 heure, 0 minute et 0 secondes. Ajouter 100 fois une seconde et afficher les différentes
heures obtenues.

� �
1ère année (2013/2014) �23 � Langage C
IUT de Villetaneuse

TP2 : Passage par adresse

Exercice 6 : Manipulation du tableau


Question 6.1 : Écrire une fonction calculant le nombre d’entiers pairs et le nombre d’entiers
impairs d’un tableau d’entiers.
Question 6.2 : Réaliser une fonction void tabechange(int tab1[], int tab2[], int len)
qui, étant donnés deux tableaux de nombres entiers tab1 et tab2 de même longueur len, permute
leurs contenus en utilisant la fonction echange définie dans l’exemple 1 de la section 2.1.
Question 6.3 : Réaliser une fonction void tabinverse(int tab[], int len) qui renverse
un tableau en utilisant la fonction echange définie dans l’exemple 1 de la section 2.1 .
Question 6.4 : Reprendre la question précédente en utilisant cette fois la notation pointeur pour
passer l’adresse du tableau en argument.

Exercice 7 : Création d’une pile de taille maximale fixe


En informatique, une pile (en anglais stack) est une structure de données fondée sur le principe
« dernier arrivé, premier sorti » (ou LIFO pour Last In, First Out), ce qui veut dire que les
derniers éléments ajoutés à la pile seront les premiers à être récupérés. Le fonctionnement est celui
d’une pile d’assiettes : on ajoute des assiettes sur la pile, et on les récupère dans l’ordre inverse,
en commençant par la dernière ajoutée 2 .
Dans cet exercice, les éléments empilés seront des caractères (char). La pile pourra contenir
au plus 100 caractères. Les fonctions possibles pour la pile seront :
– initialisePile(...) qui initialise la pile ou la vide si cette dernière contenait quelque
chose,
– estVide(...) qui renvoie 1 si la pile est vide, 0 sinon,
– estPleine(...) qui renvoie 1 si la pile est pleine (contient 100 caractères), 0 sinon,
– empile(...) qui ajoute un caractère en haut de la pile si cette dernière n’est pas vide,
– depile(...) qui supprime dans la pile le dernier caractère et retourne sa valeur (si la pile
n’est pas vide).
Question 7.1 : Définir une structure pile permettant d’empiler des caractères.
Question 7.2 : Implémenter les différentes fonctions données précédemment (les types retournés
et les paramètres ne sont pas donnés).
Question 7.3 : Écrire une fonction affichePile() qui affiche le contenu de la pile.
Question 7.4 : Écrire un programme principal qui, à l’aide d’une pile, empile tous les caractères
du premier argument du programme puis dépile tous les caractères en les affichant. L’affichage
doit donc être l’inverse du paramètre du programme. Ainsi, si le programme (appelé pile) est
exécuté selon ./pile bonjour, l’affichage doit donc être : ruojnob.

2. Définition donnée par Wikipédia

� �
Langage C �24 � 1ère année (2013/2014)

S-ar putea să vă placă și