Pointeurs ou tableaux ?

L’arithmétique des pointeurs rend leur utilisation si pratique qu’on s’en sert souvent dans des situations où, dans d’autres langages, on aurait utilisé des tableaux.

C’est déjà le cas avec les chaînes de caractères utilisées sous le type char*. Mais la remarque s’applique à tous les tableaux, et plus particulièrement à ceux qui sont placés en mémoire dynamique (voir dans le chapitre 5, sur ce sujet), ou dont la taille n’est pas connue à l’avance, comme par exemple dans les appels de fonctions. Ainsi, la procédure affiche_tableau, vue au paragraphe sur les tableaux, pourrait être réécrite ainsi :

void affiche_tableau(unsigned n, int *tab)
          {
              for (int *fin = tab +n; tab < fin; tab++)
                  cout << *tab << '\t';
          }

On notera que l’incrémentation de tab n’est pas fautive et ne provoque pas d’effet de bord (voir chapitre 5). On remarquera aussi que le pointeur initialisé dans la boucle for n’est pas celui qui est incrémenté : mais cela est parfaitement permis.

Comme les tableaux sont en fait des pointeurs sur leur premier élément, on peut encore écrire :

int tableau[20];
affiche_tableau(20, tableau);

tout comme avec l’ancienne version.

La question qui vient naturellement à l’esprit est la suivante : y a-t-il gain ou perte par rapport à l’ancienne version (qui utilisait un entier d’index i et affichait les tableau[i] successifs) ? Bien qu’a priori on puisse penser que ces deux implémentations sont équivalentes, il n’en est rien en réalité, il y a un gain.

En effet, lorsqu’on écrit :

cout << tableau[i];

la machine fait deux opérations : connaissant l’adresse de tableau et i , elle multiplie le second par la taille des entiers et ajoute le résultat à tableau (pointeur sur l’élément 0), obtenant ainsi une adresse qui est celle de l’élément d’indice i. L’écriture précédente est donc équivalente à :

cout << *(int*)((char*)tableau +  i * sizeof(int));

ce qui est évidemment plus complexe (donc plus lent) que :

cout << *tab;

De telles considérations justifient souvent l’emploi de pointeurs à la place des tableaux, en particulier pour les opérations que l’on souhaite rapides, comme celles sur les chaînes de caractères par exemple.

Reste à savoir comment l’on définit des tableaux multidimensionnels de cette façon. C’est déjà moins pratique ; il existe deux possibilités. Soit on considère qu’un tableau multidimensionnel est un tableau unidimensionnel dont on numérote spécialement les éléments, auquel cas on utilise un pointeur simple. Par exemple les instructions suivantes, qui écrivent entièrement une matrice de 3*5 entiers :

int tableau[3][5] = ... // initialiser...
int i = 0, j = 0;
while (i < 3) {
       cout << tableau[i][j] << (j < 4 ? '\t' : '\n');
       if (++j == 5) { i++; j = 0; }
}

deviennent :

int *tab = ... // initialiser...
int *fin = tab + 3*5;
for (int *t = tab; t < fin; t++)
     cout << *t << ( (t -tab+1)%5 ? '\t' : '\n');

On notera que certaines opérations comme ici le choix entre tabulation et fin de ligne sont assez difficiles. En outre l’accès à un élément particulier du tableau est peu pratique, puisqu’il faut écrire tab +i*5 +j là où tableau[i][j] suffisait précédemment. En particulier, il est nécessaire de connaître la largeur du tableau (5).

La deuxième approche consiste à dire que le tableau multidimensionnel est un tableau de tableaux. On peut donc le remplacer par un pointeur sur un pointeur :

int **tbp = ...        // initialiser...
int **fin = tbp +3;
for (int **tp = tbp; tp < tbp; tp++) {
          int *stop = *tp +4;
          for (int *p = *tp; p <= stop; p++)
               cout << *p << (p < stop ? '\t' : '\n');
          }

Cette solution a des avantages et des inconvénients. En général, l’initialisation (omise ici) est plus lourde, car il faut attribuer la place au pointeur de pointeurs, puis à chaque pointeur séparément. En contrepartie, l’accès à des éléments fixes peut être plus facile. Cette structure permet en outre une utilisation partielle seulement, c’est-à-dire que certains pointeurs peuvent rester à zéro ; cela fait gagner de la mémoire. Un usage pratique, lorsqu’il est secondé par la POO.

Précédent Précédent Sommaire Sommaire Suivant Suivant