Programmation C-C++/Référencement, indirection

En cours d'importationlink={{{link}}}

Ce livre est en cours de copie depuis le site http://casteyde.christian.free.fr/online/cours_cpp/ qui le fournit sous licence GFDL.

Cours de C/C++
^
Pointeurs et références
Notion d'adresse
Notion de pointeur
Référencement, indirection
Notion de référence
Lien entre les pointeurs et les références
Passage de paramètres par variable ou par valeur
Références et pointeurs constants et volatiles
Arithmétique des pointeurs
Utilisation des pointeurs avec les tableaux
Les chaînes de caractères : pointeurs et tableaux à la fois !
Allocation dynamique de mémoire
Pointeurs et références de fonctions
Paramètres de la fonction main - ligne de commande
DANGER

Livre original de C. Casteyde

Référencement, indirection

modifier

Un pointeur ne servirait strictement à rien s'il n'y avait pas de possibilité d'accéder à l'adresse d'une variable ou d'une fonction (on ne pourrait alors pas l'initialiser) ou s'il n'y avait pas moyen d'accéder à l'objet référencé par le pointeur (la variable pointée ne pourrait pas être manipulée ou la fonction pointée ne pourrait pas être appelée).

Ces deux opérations sont respectivement appelées référencement et indirection (ou déréférencement). Il existe deux opérateurs permettant de récupérer l'adresse d'un objet et d'accéder à l'objet pointé. Ces opérateurs sont respectivement & et *.

Il est très important de s'assurer que les pointeurs que l'on manipule sont tous initialisés (c'est-à-dire contiennent l'adresse d'un objet valide, et pas n'importe quoi). En effet, accéder à un pointeur non initialisé revient à lire ou, plus grave encore, à écrire dans la mémoire à un endroit complètement aléatoire (selon la valeur initiale du pointeur lors de sa création). En général, on initialise les pointeurs dès leur création, ou, s'ils doivent être utilisés ultérieurement, on les initialise avec le pointeur nul. Cela permettra de faire ultérieurement des tests sur la validité du pointeur ou au moins de détecter les erreurs. En effet, l'utilisation d'un pointeur initialisé avec le pointeur nul génère souvent une faute de protection du programme, que tout bon débogueur est capable de détecter. Le pointeur nul se note NULL.

Note : NULL est une macro définie dans le fichier d'en-tête stdlib.h. En C, elle représente la valeur d'une adresse invalide. Malheureusement, cette valeur peut ne pas être égale à l'adresse 0 (certains compilateurs utilisent la valeur -1 pour NULL par exemple). C'est pour cela que cette macro a été définie, afin de représenter, selon le compilateur, la bonne valeur. Voir le chapitre "le préprocesseur" pour plus de détails sur les macros et sur les fichiers d'en-tête.

La norme du C++ fixe la valeur nulle des pointeurs à 0. Par conséquent, les compilateurs C/C++ qui définissent NULL comme étant égal à -1 posent un problème de portabilité certain, puisque un programme C qui utilise NULL n'est plus valide en C++. Par ailleurs, un morceau de programme C++ compilable en C qui utiliserait la valeur 0 ne serait pas correct en C.

Il faut donc faire un choix : soit utiliser NULL en C et 0 en C++, soit utiliser NULL partout, quitte à redéfinir la macro NULL pour les programmes C++ (solution qui me semble plus pratique).

Exemple - Déclaration de pointeurs

modifier
int i=0; /* Déclare une variable entière. */
int *pi; /* Déclare un pointeur sur un entier. */
pi=&i; /* Initialise le pointeur avec l'adresse de cette
                variable. */
*pi = *pi+1; /* Effectue un calcul sur la variable pointée par pi,
                 c'est-à-dire sur i lui-même, puisque pi contient
                 l'adresse de i. */

              /* À ce stade, i ne vaut plus 0, mais 1. */

Il est à présent facile de comprendre pourquoi il faut répéter l'étoile dans la déclaration de plusieurs pointeurs :

int *p1, *p2, *p3;

signifie syntaxiquement : p1, p2 et p3 sont des pointeurs d'entiers, mais aussi *p1, *p2 et *p3 sont des entiers.

Si l'on avait écrit :

int *p1, p2, p3;

seul p1 serait un pointeur d'entier. p2 et p3 seraient des entiers.

L'accès aux champs d'une structure par le pointeur sur cette structure se fera avec l'opérateur '->', qui remplace '(*).'.

Exemple - Utilisation de pointeurs de structures

modifier
struct Client
{
    int Age;
};

Client structure1;
Client *pstr = &structure1;
pstr->Age = 35; /* On aurait pu écrire (*pstr).Age=35; */