Nous avons déjà été amené à utiliser l'opérateur & pour désigner l'adresse d'une lvalue. D'une manière
générale, le langage C permet de manipuler des adresses par l'intermédiaire de variables nommées
"pointeurs".
En guise d'introduction à cette nouvelle notion, considérons les instructions :
- Code:
int * ad ;
int n ;
n = 20 ;
ad = &n ;
*ad = 30 ;
que * est un opérateur qui désigne le contenu de l'adresse qui le suit. Ainsi, à titre "mnémonique", on peut
dire que cette déclaration signifie que *ad, c'est-à dire l'objet d'adresse ad, est de type int ; ce qui signifie
bien que ad est l'adresse d'un entier.
L'instruction : ad = &n ; affecte à la variable ad la valeur de l'expression &n. L'opérateur & (que nous
avons déjà utilisé avec scanf) est un opérateur unaire qui fournit comme résultat l'adresse de son opérande.
Ainsi, cette instruction place dans la variable ad l'adresse de la variable n.
Après son exécution.
L'instruction suivante : *ad = 30 ; signifie : affecter à la lvalue *ad la valeur 30. Or *ad représente l'entier
ayant pour adresse ad. Après exécution de cette instruction, la valeur de n devient 30. Bien entendu, ici,
nous aurions obtenu le même résultat avec :n = 30 ;
Quelques Exemple
Voici quelques exemples d'utilisation de ces deux opérateurs. Supposez que nous ayons effectué ces
déclarations :
- Code:
int * ad1, * ad2, * ad ;
int n = 10, p = 20 ;
instructions :
- Code:
ad1 = &n ;
ad2 = &p ;
* ad1 = * ad2 + 2 ;
l'expression : * ad2 + 2.
Autrement dit, elle place à l'adresse désignée par ad1 la valeur (entière) d'adresse ad2, augmentée de 2.
Cette instruction joue donc ici le même rôle que : n = p + 2 ;
De manière comparable, l'expression : * ad1 += 3
jouerait le même rôle que : n = n + 3
et l'expression : ( * ad1 ) ++
jouerait le même rôle que n++
Remarque :
1) Si ad est un pointeur, les expressions ad et *ad sont des lvalue ; autrement dit ad et *ad sont
modifiables. En revanche, il n'en va pas de même de &ad. En effet, cette expression désigne, non plus une
variable pointeur comme ad, mais l'adresse de la variable ad telle qu'elle a été définie par le compilateur.
Cette adresse est nécessairement fixe et il ne saurait être question de la modifier (la même remarque
Langage C
21
s'appliquerait à &n, où n serait une variable scalaire quelconque). D'une manière générale, des
expressions telles que : (&ad)++ ou (&p)++ seront rejetées à la compilation.
2) Une déclaration telle que : int * ad réserve un emplacement pour un pointeur sur un entier. Elle ne
réserve pas en plus un emplacement pour un tel entier.
TABLEAUX ET POINTEUR
En langage C, l'identificateur d'un tableau, lorsqu'il est employé seul (sans indices à sa suite), est
considéré comme un pointeur (constant) sur le début du tableau.
Tableau à un indice
Supposons, par exemple, que l'on effectue la déclaration suivante :
int t[10]
La notation t est alors totalement équivalente à &t[0]. L'identificateur t est considéré comme étant de type
pointeur sur le type correspondant aux éléments du tableau, c'est-à-dire, ici, int*.
Ainsi, voici quelques exemples de notations équivalentes :
t+1 ---------------- &t[1]
t+i ---------------- &t[i]
t[i]----------------- * (t+i)
Pour illustrer ces nouvelles possibilités de notation, voici plusieurs façons de placer la valeur 1 dans
chacun des 10 éléments de notre tableau t :
- Code:
int i ;
for (i=0 ; i<10 ; i++)
* (t+i) = 1 ;
ou:
int i ;
int * p :
for (p=t, i=0 ; i<10 ; i++, p++)
* p = 1 ;
En effet, il ne faut pas perdre de vue que le symbole t représente une adresse constante (t est une
constante de type pointeur sur des entiers). Autrement dit, une expression telle que t++ aurait été invalide,
au même titre que, par exemple, 3++. Un nom de tableau est un pointeur constant ; ce n'est pas une lvalue.
Remarque importante :
Nous venons de voir que la notation t[i] est équivalente à *(t+i) lorsque t est déclaré comme un tableau.
En fait, cela reste vrai, quelle que soit la manière dont t a été déclaré. Ainsi, avec :
int * t ; les deux notations précédentes resteraient équivalentes. Autrement dit, on peut utiliser t[i] dans un
programme où t est simplement déclaré comme un pointeur.
Tableau à plusieurs indices
Lorsque le compilateur rencontre une déclaration telle que :
int t[3] [4] ;
il considère en fait que t désigne un tableau de 3 éléments, chacun de ces éléments étant lui-même un
tableau de 4 entiers.
Autrement dit, si t représente bien l'adresse de début de notre tableau t, il n'est plus de type int * (comme
c'était le cas pour un tableau à un indice) mais d'un type "pointeur sur des blocs de 4 entiers", type qui
devrait se noter théoriquement :
int [4] *
Dans ces conditions, une expression telle que t+1 correspond à l'adresse de t, augmentée de 4 entiers (et
non plus d'un seul !).
Exemple
Le tableau M à deux dimensions est défini comme suit:
- Code:
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{10,11,12,13,14,15,16,17,18,19},
{20,21,22,23,24,25,26,27,28,29},
{30,31,32,33,34,35,36,37,38,39}};
a la valeur: {0,1,2,3,4,5,6,7,8,9}. L'expression(M+1) est l'adresse du deuxième élément du tableau et
pointe sur M[1] qui a la valeur: {10,11,12,13,14,15,16,17,18,19}.
Explication
Au sens strict du terme, un tableau à deux dimensions est un tableau unidimensionnel dont chaque
composante est un tableau unidimensionnel. Ainsi, le premier élément de la matrice M est le vecteur
{0,1,2,3,4,5,6,7,8,9}, le deuxième élément est {10,11,12,13,14,15,16,17,18,19} et ainsi de suite.
L'arithmétique des pointeurs qui respecte automatiquement les dimensions des éléments conclut
logiquement que:
M+I désigne l'adresse du tableau M[I]
Problème
Comment pouvons-nous accéder à l'aide de pointeurs aux éléments de chaque composante du tableau, c.àd.:
aux éléments M[0][0], M[0][1], ... , M[3][9] ?
Une solution consiste à convertir la valeur de M (qui est un pointeur sur un tableau du type int) en un
pointeur sur int. On pourrait se contenter de procéder ainsi:
- Code:
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{10,11,12,13,14,15,16,17,18,19},
{20,21,22,23,24,25,26,27,28,29},
{30,31,32,33,34,35,36,37,38,39}};
int *P;
P = M; /* conversion automatique */
&M[0][0]. (Remarquez bien que l'adresse transmise reste la même, seule la nature du pointeur a changé).
Cette solution n'est pas satisfaisante à cent pour-cent: Généralement, on gagne en lisibilité en explicitant
la conversion mise en oeuvre par l'opérateur de conversion forcée ("cast"), qui évite en plus des messages
d'avertissement de la part du compilateur.
Solution
Voici finalement la version que nous utiliserons:
- Code:
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{10,11,12,13,14,15,16,17,18,19},
{20,21,22,23,24,25,26,27,28,29},
{30,31,32,33,34,35,36,37,38,39}};
int *P;
P = (int *)M; /* conversion forcée */
traiter M à l'aide du pointeur P comme un tableau unidimensionnel de dimension 4*10.
Exemple
Les instructions suivantes calculent la somme de tous les éléments du tableau M:
- Code:
int M[4][10] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{10,11,12,13,14,15,16,17,18,19},
{20,21,22,23,24,25,26,27,28,29},
{30,31,32,33,34,35,36,37,38,39}};
int *P;
int I, SOM;
P = (int*)M;
SOM = 0;
for (I=0; I<40; I++)
SOM += *(P+I);
Lors de l'interprétation d'un tableau à deux dimensions comme tableau unidimensionnel il faut
calculer avec le nombre de colonnes indiqué dans la déclaration du tableau.
Exemple
Pour la matrice A, nous réservons de la mémoire pour 3 lignes et 4 colonnes, mais nous utilisons
seulement 2 lignes et 2 colonnes:
- Code:
int A[3][4];
A[0][0]=1;
A[0][1]=2;
A[1][0]=10;
A[1][1]=20;
L'adresse de l'élément A[I][J] se calcule alors par:
A + I*4 + J
Conclusion
Pour pouvoir travailler à l'aide de pointeurs dans un tableau à deux dimensions, nous avons besoin de
quatre données:
a) l'adresse du premier élément du tableau converti dans le type simple des éléments du tableau
b) la longueur d'une ligne réservée en mémoire
(- voir déclaration - ici: 4 colonnes)
c) le nombre d'éléments effectivement utilisés dans une ligne
(- p.ex: lu au clavier - ici: 2 colonnes)
d) le nombre de lignes effectivement utilisées
(- p.ex: lu au clavier - ici: 2 lignes)
La comparaison de pointeurs
Il ne faut pas oublier qu'en C un pointeur est défini à la fois par une adresse en mémoire et par un type.
On ne pourra donc comparer que des pointeurs de même type. Par exemple, voici, en parallèle, deux
suites d'instructions réalisant la même action :
mise à 1 des 10 éléments du tableau t :
- Code:
int t[10] ; int t[10] ;
int * p ; int i ;
for (p=t ; p<t+10 ; p++) for (i=0 ; i<10 ; i++)
*p = 1 ; t[i] = 1 ;
Jeu 28 Nov - 8:07 par arsenepoutsi
» CCNA version 4 français
Dim 31 Mar - 15:15 par kadhouceeee
» Trés important " Livre exercices en langage C ( claude delannoy) "
Jeu 27 Sep - 16:31 par latifa oustouh
» Ulead VideoStudio 11.5.0157.2 Plus + Crack Collection
Jeu 26 Avr - 9:51 par miloduvi
» Langage IDL(Interface Definition Language)
Mar 27 Sep - 13:02 par Abdelkhalek Bakkari
» demande cours PHP
Jeu 2 Juin - 11:23 par elasriahmed
» exemple page web réaliser avec du html et JavaScript
Lun 30 Mai - 4:05 par ouh.rouchan.zakaria
» cours group2_ 2année 2011
Dim 20 Mar - 15:35 par El Maligno Angelo
» Cours : Infrastructures Réseaux 2003 Server
Mar 15 Fév - 6:55 par jettjackson
» cours officiel de CCNA 2
Mar 18 Jan - 13:53 par agares