Taille fixe
1. Passer par référence
template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
En C ++, passer le tableau par référence sans perdre les informations de dimension est probablement le plus sûr, car il n'est pas nécessaire de se soucier que l'appelant passe une dimension incorrecte (drapeaux du compilateur en cas de non-correspondance). Cependant, cela n'est pas possible avec les tableaux dynamiques (magasin libre); cela ne fonctionne que pour les tableaux automatiques ( généralement empilés ), c'est-à-dire que la dimensionnalité doit être connue au moment de la compilation.
2. Passer par le pointeur
void process_2d_array_pointer(int (*array)[5][10])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < 5; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << (*array)[i][j] << '\t';
std::cout << std::endl;
}
}
L'équivalent C de la méthode précédente passe le tableau par pointeur. Cela ne doit pas être confondu avec le passage par le type de pointeur pourri du tableau (3) , qui est la méthode courante et populaire, bien que moins sûre que celle-ci mais plus flexible. Comme (1) , utilisez cette méthode lorsque toutes les dimensions du tableau sont fixes et connues au moment de la compilation. Notez que lors de l'appel de la fonction, l'adresse du tableau doit être transmise process_2d_array_pointer(&a)
et non l'adresse du premier élément par décroissance process_2d_array_pointer(a)
.
Taille variable
Celles-ci sont héritées de C mais sont moins sûres, le compilateur n'a aucun moyen de vérifier, garantissant que l'appelant passe les dimensions requises. La fonction ne dépend que de ce que l'appelant passe comme dimension (s). Celles-ci sont plus flexibles que les précédentes, car des tableaux de différentes longueurs peuvent leur être transmis invariablement.
Il faut se rappeler qu'il n'y a rien de tel que de passer un tableau directement à une fonction en C [tandis qu'en C ++, ils peuvent être passés comme référence (1) ]; (2) transmet un pointeur au tableau et non au tableau lui-même. Toujours passer un tableau tel quel devient une opération de copie de pointeur qui est facilitée par la nature du tableau de se désintégrer en un pointeur .
3. Passez (valeur) un pointeur sur le type pourri
// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
Bien que cela int array[][10]
soit autorisé, je ne le recommanderais pas par-dessus la syntaxe ci-dessus, car la syntaxe ci-dessus indique clairement que l'identifiant array
est un pointeur unique vers un tableau de 10 entiers, tandis que cette syntaxe ressemble à un tableau 2D mais est le même pointeur vers un tableau de 10 entiers. Ici, nous connaissons le nombre d'éléments dans une seule ligne (c'est-à-dire la taille de la colonne, 10 ici) mais le nombre de lignes est inconnu et doit donc être passé en argument. Dans ce cas, il y a une certaine sécurité car le compilateur peut signaler lorsqu'un pointeur vers un tableau avec une deuxième dimension non égale à 10 est passé. La première dimension est la partie variable et peut être omise. Voir ici pour la raison pour laquelle seule la première dimension peut être omise.
4. Passer un pointeur à un pointeur
// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
Encore une fois, il existe une syntaxe alternative int *array[10]
qui est la même que int **array
. Dans cette syntaxe, le [10]
est ignoré car il se désintègre en un pointeur devenant ainsi int **array
. C'est peut-être juste un signal à l'appelant que le tableau passé doit avoir au moins 10 colonnes, même si le nombre de lignes est requis. Dans tous les cas, le compilateur ne signale aucune violation de longueur / taille (il vérifie uniquement si le type transmis est un pointeur vers un pointeur), nécessitant donc à la fois le nombre de lignes et de colonnes comme paramètre est logique ici.
Remarque: (4) est l'option la moins sûre car elle ne comporte quasiment aucun contrôle de type et est la plus gênante. On ne peut légitimement passer un tableau 2D à cette fonction; C-FAQ condamne la solution de contournement habituelle int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
car cela peut potentiellement conduire à un comportement indéfini en raison de l'aplatissement de la baie. La bonne façon de passer un tableau dans cette méthode nous amène à la partie gênante, c'est-à-dire que nous avons besoin d'un tableau supplémentaire (de substitution) de pointeurs avec chacun de ses éléments pointant vers la ligne respective du tableau réel à passer; ce substitut est ensuite transmis à la fonction (voir ci-dessous); tout cela pour faire le même travail que les méthodes ci-dessus qui sont plus sûres, plus propres et peut-être plus rapides.
Voici un programme pilote pour tester les fonctions ci-dessus:
#include <iostream>
// copy above functions here
int main()
{
int a[5][10] = { { } };
process_2d_array_template(a);
process_2d_array_pointer(&a); // <-- notice the unusual usage of addressof (&) operator on an array
process_2d_array(a, 5);
// works since a's first dimension decays into a pointer thereby becoming int (*)[10]
int *b[5]; // surrogate
for (size_t i = 0; i < 5; ++i)
{
b[i] = a[i];
}
// another popular way to define b: here the 2D arrays dims may be non-const, runtime var
// int **b = new int*[5];
// for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
process_pointer_2_pointer(b, 5, 10);
// process_2d_array(b, 5);
// doesn't work since b's first dimension decays into a pointer thereby becoming int**
}