Ces deux types de données présentent des similarités et des différences importantes. Si l'on définit
int t[4]; int *p;
il y a allocation de mémoire, dans le premier cas, pour 4 entiers, et dans le second, pour une adresse. On a vu cependant que la valeur de t était l'adresse du bloc alloué. On peut donc affecter cette adresse à p, ce qui a pour effet que *p et t[0] désigneront le même objet :
p = t;
L'affectation inverse t = p est illégale car t est une constante, pas une variable. Au niveau des valeurs, pointeurs d'int et tableaux d'int sont de même type. En outre, si t est déclaré comme un tableau d'éléments constants, l'affectation ou l'initialisation p = t ne peut se faire que si le pointeur est aussi un pointeur vers un objet constant (sinon, la constance de l'objet est perdue par affectation) :
const int t[4]; const int *p = t; int *q = t; // ERREUR
Ce qui est vrai pour l'affectation et l'initialisation l'est également pour le passage d'argument des fonctions. Une fonction void g(int *p) sur des pointeurs peut être appelée avec un tableau en argument, g(t), puisque ceci correspond à une initialisation de la forme int *p = t;.
Contrairement à ce nous venons d'écrire pour l'affectation t = p;, qui est illégale, il se trouve que dans la situation inverse, une fonction void f(int t[]) sur les tableaux, peut être appelée avec un pointeur en argument, f(p). Ceci provient du fait que ce n'est pas l'objet tableau qui est passé en argument, mais seulement son adresse, et que cette adresse est une valeur de type int*. Les deux déclarations suivantes de fonctions sont donc exactement équivalentes :
void f(int t[]); // ÉQUIVALENT void f(int *t); // ÉQUIVALENT
Il en est de même des deux définitions suivantes, pour des tableaux bidimensionnels (les paranthèses autour de *t sont obligatoires) :
void g(int t[][3]); // ÉQUIVALENT void g(int (*t)[3]); // ÉQUIVALENT
L'accès par indexation à un tableau (unidimensionnel) et la
déréférenciation sont aussi des opérations équivalentes : si p
est un pointeur, *p équivaut à p[0], *(p+1)
équivaut à p[1], etc. Inversement si t est un
tableau, t équivaut à &t[0]
, t+1 équivaut à
&t[1]
, etc. Par exemple, les initialisations suivantes du
pointeur p sont équivalentes et permettent de
désigner le sous-tableau décalé, commençant par le deuxième élément de
t :
double t[4]; double *p = &t[1]; // ÉQUIVALENT double *p = t+1; // ÉQUIVALENT
Il en résulte des équivalences analogues pour des tableaux pluridimensionnels :
int t[2][3] = { {1, 2, 3}, {4, 5, 6} }; int (*p)[3] = t+1; // *p désigne la seconde ligne de t cout << p[0][2] << endl; // ÉQUIVALENT cout << (*p)[2] << endl; // ÉQUIVALENT cout << t[1]2[] << endl; // ÉQUIVALENT