Vous êtes sur la page 1sur 10

Problème : je veux écrire une fonction qui renvoie deux valeurs.

« Impossible » !!
En effet, on ne peut renvoyer qu'une valeur par fonction :
int fonction()
{
return valeur;
}

Si on indique int, on renverra un nombre de type int (grâce à l'instruction return).


On peut aussi écrire une fonction qui ne renvoie aucune valeur avec le mot-clé : void:

void fonction()
{

Mais renvoyer deux valeurs à la fois… c'est impossible. On ne peut pas faire deux return.
Supposons que je veuille écrire une fonction à laquelle on envoie un nombre de minutes.

Celle-ci renverrait le nombre d'heures et minutes correspondantes :


1. si on envoie 45, la fonction renvoie 0 heure et 45 minutes ;
2. si on envoie 60, la fonction renvoie 1 heure et 0 minutes ;
3. si on envoie 90, la fonction renvoie 1 heure et 30 minutes.

#include <stdio.h>

#include <stdlib.h>

void decoupeMinutes(int heures, int minutes);

int main(int argc, char *argv[])

int heures = 0, minutes = 90;

/* On a une variable minutes qui vaut 90.

Après appel de la fonction, je veux

"heures"= 1 et que "minutes" = 30 */


decoupeMinutes(heures, minutes);

printf("%d heures et %d minutes", heures, minutes);

return 0;

void decoupeMinutes(int heures, int minutes)

heures = minutes / 60; // 90 / 60 = 1

minutes = minutes % 60; // 90 % 60 = 30

Résultat :

0 heures et 90 minutes

Voilà, le problème est posé. Comment les pointeurs vont-ils nous


permettre de le résoudre ?
La mémoire, une question d'adresse
Quand vous créez une variable age de type int par exemple, en tapant ça :

int age = 10;

printf("La variable age vaut : %d", age);

Résultat à l'écran :
La variable age vaut : 10

On sait afficher la valeur de la variable, mais saviez-vous que l'on peut


aussi afficher l'adresse correspondante ?
Tapez donc :

printf("L'adresse de la variable age est : %p", &age);

Résultat :
L'adresse de la variable age est : 0023FF74

 age: désigne la valeur de la variable ;


 &age: désigne l'adresse de la variable.

Avec age, l'ordinateur va lire la valeur de la variable en mémoire et vous renvoie cette valeur.
Avec &age, votre ordinateur vous dit en revanche à quelle adresse se trouve la variable.

Créer un pointeur
Pour créer une variable de type pointeur, on doit rajouter le symbole * devant le nom de la
variable.

int *monPointeur;
Initialisation du pointeur :
int *monPointeur = NULL;
int age = 10;
int *pointeurSurAge = &age;

La première ligne signifie : « Créer une variable de type int dont la valeur vaut 10 ». La
seconde ligne signifie : « Créer une variable de type pointeur dont la valeur vaut l'adresse de
la variable age».
La seconde ligne fait donc deux choses à la fois.

On peut la découper en deux temps :

int age = 10;


int *pointeurSurAge; // 1) signifie "Je crée un pointeur"
pointeurSurAge = &age; // 2) signifie "pointeurSurAge contient l'adresse de la variable
age"

Vocabulaire : on dit que le pointeurpointeurSurAgepointe sur la variableage.


La fig. suivante résume ce qu'il s'est passé dans la mémoire.
int age = 10;
int *pointeurSurAge = &age;

printf("%d", pointeurSurAge);

177450
On demande la valeur de pointeurSurAge, et sa valeur c'est l'adresse de la variable age
(177450).
Comment faire pour demander à avoir la valeur de la variable se trouvant à l'adresse indiquée
dans pointeurSurAge? Il faut placer le symbole * devant le nom du pointeur :

int age = 10;


int *pointeurSurAge = &age;

printf("%d", *pointeurSurAge);

10
En plaçant le symbole*devant le nom du pointeur, on accède à la valeur de la variableage.
Si au contraire on avait utilisé le symbole & devant le nom du pointeur, on aurait obtenu
l'adresse à laquelle se trouve le pointeur (ici, c'est 3).
Qu'est-ce qu'on y gagne ? On a simplement réussi à compliquer les choses ici. On n'avait pas
besoin d'un pointeur pour afficher la valeur de la variable age!
À retenir absolument
Voici ce qu'il faut avoir compris et ce qu'il faut retenir pour la suite de ce chapitre :
 sur une variable, comme la variableage:
o age signifie : « Je veux la valeur de la variableage»,
o &age signifie : « Je veux l'adresse à laquelle se trouve la variableage» ;
 sur un pointeur, commepointeurSurAge:
o pointeurSurAge signifie : « Je veux la valeur depointeurSurAge» (cette
valeur étant une adresse),
o *pointeurSurAge signifie : « Je veux la valeur de la variable qui se trouve à
l'adresse contenue danspointeurSurAge».
Envoyer un pointeur à une fonction
Le gros intérêt des pointeurs (mais ce n'est pas le seul) est qu'on peut les envoyer à des
fonctions pour qu'ils modifient directement une variable en mémoire, et non une copie comme
on l'a vu.
Exemple :

void triplePointeur(int *pointeurSurNombre);

int main(int argc, char *argv[])


{
int nombre = 5;

triplePointeur(&nombre); // On envoie l'adresse de nombre à la


fonction
printf("%d", nombre); // On affiche la variable nombre. La
fonction a directement modifié la valeur de la variable car elle
connaissait son adresse

return 0;
}

void triplePointeur(int *pointeurSurNombre)


{
*pointeurSurNombre =*pointeurSurNombre* 3; // On multiplie par 3
la valeur de nombre
}

15
La fonction triplePointeur prend un paramètre de type int*(c'est-à-dire un pointeur sur
int).
Voici ce qu'il se passe dans l'ordre, en partant du début du main:

1. une variablenombreest créée dans lemain. On lui affecte la valeur 5.


2. on appelle la fonctiontriplePointeur. On lui envoie en paramètre l'adresse de notre
variablenombre;
3. la fonction triplePointeur reçoit cette adresse dans pointeurSurNombre. À
l'intérieur de la fonction triplePointeur, on a donc un pointeur
pointeurSurNombrequi contient l'adresse de la variable nombre;
4. maintenant qu'on a un pointeur sur nombre, on peut modifier directement la variable
nombre en mémoire ! Il suffit d'utiliser *pointeurSurNombre pour désigner la
variable nombre! Pour l'exemple, on fait un simple test : on multiplie la variable
nombre par 3 ;
5. de retour dans la fonction main, notre nombre vaut maintenant 15 car la fonction
triplePointeur a modifié directement la valeur de nombre.

En utilisant des pointeurs, on peut modifier la valeur de plusieurs variables en mémoire (on
peut donc « renvoyer plusieurs valeurs »). Nous ne sommes plus limités à une seule valeur !
Quel est l'intérêt maintenant d'utiliser un return dans une fonction si on peut se servir des
pointeurs pour modifier des valeurs ?
Ça dépendra de vous et de votre programme. C'est à vous de décider. Il faut savoir que les
return sont bel et bien toujours utilisés en C. Le plus souvent, on s'en sert pour renvoyer ce
qu'on appelle un code d'erreur : la fonction renvoie 1 (vrai) si tout s'est bien passé, et 0 (faux)
s'il y a eu une erreur pendant le déroulement de la fonction.
Une autre façon d'envoyer un pointeur à une fonction
Dans le code source qu'on vient de voir, il n'y avait pas de pointeur dans la fonction main.
Juste une variable nombre. Le seul pointeur qu'il y avait vraiment était dans la fonction
triplePointeur(de type int*).

Il faut absolument que vous sachiez qu'il y a une autre façon d'écrire le code précédent, en
ajoutant un pointeur dans la fonction main:

void triplePointeur(int *pointeurSurNombre);

int main(int argc, char *argv[])


{
int nombre = 5;
int *pointeur = &nombre; // pointeur prend l'adresse de nombre

triplePointeur(pointeur); // On envoie pointeur (l'adresse de


nombre) à la fonction
printf("%d", *pointeur); // On affiche la valeur de nombre avec
*pointeur

return 0;
}

void triplePointeur(int *pointeurSurNombre)


{
*pointeurSurNombre *= 3; // On multiplie par 3 la valeur de
nombre
}

Comparez bien ce code source avec le précédent. Il y a de subtiles différences et pourtant le


résultat est strictement le même :
15
Ce qui compte, c'est d'envoyer l'adresse de la variable nombre à la fonction. Or, pointeur
vaut l'adresse de la variable nombre, donc c'est bon de ce côté ! On le fait seulement d'une
manière différente en créant un pointeur dans la fonction main.
Dans le printf (et c'est juste pour l'exercice), j'affiche le contenu de la variable nombre en
tapant*pointeur. Notez qu'à la place, on peut écrire nombre: le résultat aurait été identique
car *pointeuretnombre désignent la même chose dans la mémoire.

Dans le programme « Plus ou Moins », nous avons utilisé des pointeurs sans vraiment le
savoir. C'était en fait en appelant la fonction scanf. En effet, cette fonction a pour rôle de lire
ce que l'utilisateur a entré au clavier et de renvoyer le résultat. Pour que la fonction puisse
modifier directement le contenu de votre variable afin d'y placer la valeur tapée au clavier,
elle a besoin de l'adresse de la variable :

int nombre = 0;
scanf("%d", &nombre);

La fonction travaille avec un pointeur sur la variable nombre et peut ainsi modifier
directement le contenu de nombre.
Comme on vient de le voir, on pourrait créer un pointeur qu'on enverrait à la fonction scanf:

int nombre = 0;
int *pointeur = &nombre;
scanf("%d", pointeur);
Attention à ne pas mettre le symbole & devant pointeur dans la fonction scanf! Ici,
pointeur contient lui-même l'adresse de la variable nombre, pas besoin de mettre un&! Si
vous faisiez ça, vous enverriez l'adresse où se trouve le pointeur : or c'est de l'adresse de
nombre dont on a besoin.

Solution de problème de démarrage :


void decoupeMinutes(int* pointeurHeures, int* pointeurMinutes);

int main(int argc, char *argv[])

int heures = 0, minutes = 90;

// On envoie l'adresse de heures et minutes

decoupeMinutes(&heures, &minutes);

// Cette fois, les valeurs ont été modifiées !

printf("%d heures et %d minutes", heures, minutes);

return 0;

void decoupeMinutes(int* pointeurHeures, int* pointeurMinutes)

/* Attention à ne pas oublier de mettre une étoile devant le nom

des pointeurs ! Comme ça, vous pouvez modifier la valeur des variables,

et non leur adresse ! Vous ne voudriez pas diviser des adresses,

n'est-ce pas ? ;o) */

*pointeurHeures = *pointeurMinutes / 60;

*pointeurMinutes = *pointeurMinutes % 60;

}
Résultat :

1 heures et 30 minutes

Explication :

1. Les variables heures et minutes sont créées dans le main.


2. On envoie à la fonction decoupeMinutes l'adresse de heures et minutes.
3. La fonction decoupeMinutes récupère ces adresses dans des pointeurs appelés
pointeurHeures et pointeurMinutes. Notez que là encore, le nom importe peu.
4. La fonction decoupeMinutes modifie directement les valeurs des variables heures
et minutes en mémoire car elle possède leurs adresses dans des pointeurs. La seule
contrainte, c'est qu'il faut impérativement mettre une étoile devant le nom des
pointeurs si on veut modifier la valeur de heures et de minutes. Si on n'avait pas fait
ça, on aurait modifié l'adresse contenue dans les pointeurs, ce qui n'aurait servi… à
rien.

En résumé

 Chaque variable est stockée à une adresse précise en mémoire.


 Les pointeurs sont semblables aux variables, à ceci près qu'au lieu de stocker un
nombre ils stockent l'adresse à laquelle se trouve une variable en mémoire.
 Si on place un symbole&devant un nom de variable, on obtient son adresse au lieu de
sa valeur (ex. :&age).
 Si on place un symbole*devant un nom de pointeur, on obtient la valeur de la variable
stockée à l'adresse indiquée par le pointeur.
 Les pointeurs constituent une notion essentielle du langage C, mais néanmoins un peu
complexe au début. Il faut prendre le temps de bien comprendre comment ils
fonctionnent car beaucoup d'autres notions sont basées dessus.