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 l'information sur la dimension est probablement le plus sûr, puisqu'il n'y a pas à s'inquiéter que l'appelant passe une dimension incorrecte (le compilateur signale une mauvaise correspondance). Cependant, cela n'est pas possible avec les tableaux dynamiques (freestore) ; cela fonctionne pour les tableaux automatiques ( vivant généralement en pile ) uniquement, c'est-à-dire que la dimensionnalité doit être connue au moment de la compilation.
2. Passage 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 consiste à passer le tableau par pointeur. Ceci ne doit pas être confondu avec le passage par le type de pointeur décomposé du tableau (3) qui est la méthode la plus courante et la plus populaire, bien que moins sûre que celle-ci mais plus flexible. Comme (1) Cette méthode est utilisée 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 passée process_2d_array_pointer(&a)
et non l'adresse du premier élément par désintégration process_2d_array_pointer(a)
.
Taille variable
Ils sont hérités du C mais sont moins sûrs, le compilateur n'a aucun moyen de vérifier, de garantir que l'appelant passe les dimensions requises. La fonction dépend uniquement de ce que l'appelant transmet comme dimension(s). Elles sont plus flexibles que les précédentes puisque des tableaux de différentes longueurs peuvent leur être transmis de manière invariable.
Il faut se rappeler qu'il n'est pas possible de passer un tableau directement à une fonction en C [alors qu'en C++, on peut le passer comme une référence]. (1) ] ; (2) passe un pointeur sur le tableau et non le tableau lui-même. Transmettre un tableau tel quel devient une opération de type pointeur-copie qui est facilitée par la fonction la nature du tableau de se décomposer en un pointeur .
3. Passez par (valeur) un pointeur vers le type décomposé
// 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 int array[][10]
est autorisé, je ne le recommanderais pas par rapport à la syntaxe ci-dessus, car cette dernière indique clairement que l'identifiant array
est un pointeur unique vers un tableau de 10 entiers, alors que cette syntaxe regarde comme s'il s'agissait d'un tableau 2D mais c'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é puisque le compilateur peut signaler qu'un pointeur vers un tableau dont la deuxième dimension n'est pas égale à 10 est passé. La première dimension est la partie variable et peut être omise. Voir ici pour le raisonnement sur la raison pour laquelle seule la première dimension est autorisée à être omise.
4. Passer par 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;
}
}
Là encore, il existe une syntaxe alternative de int *array[10]
qui est identique à int **array
. Dans cette syntaxe, le [10]
est ignorée car elle se transforme en pointeur et devient ainsi int **array
. Peut-être est-ce juste une indication à 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 pas les violations de longueur/taille (il vérifie seulement si le type passé est un pointeur vers un pointeur), donc exiger à la fois le nombre de lignes et de colonnes comme paramètre est logique ici.
Note : (4) est l'option la moins sûre puisqu'il n'a pratiquement aucun contrôle de type et qu'il est le plus gênant. On ne peut pas légitimement passer un tableau 2D à cette fonction ; C-FAQ condamne la solution de contournement habituelle consistant à faire int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
comme il peut potentiellement conduire à un comportement non défini en raison de l'aplatissement du réseau. 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 (substitut) de pointeurs dont chaque élément pointe vers la ligne respective du tableau réel à passer ; ce substitut est ensuite passé à 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**
}
2 votes
Ne peut pas convertir le paramètre 3 de 'double [10][10]' en 'double **'.
4 votes
El réponse acceptée ne montre que 2 techniques [son (2) y (3) sont les mêmes] mais il y a 4 façons uniques de passer un tableau 2D à une fonction .
1 votes
Strictement parlant, oui, ce ne sont pas des tableaux 2D, mais cette convention (bien que menant à l'UB) d'avoir un tableau de pointeurs, chacun pointant vers un tableau (1D), semble être prévalente :( Avoir un tableau 1D aplati de longueur m x n, avec des fonctions/classes d'aide pour émuler un tableau 2D est peut-être mieux.
1 votes
LE PLUS FACILE -
func(int* mat, int r, int c){ for(int i=0; i<r; i++) for(int j=0; j<c; j++) printf("%d ", *(mat+i*c+j)); }
. Appelez ça commeint mat[3][5]; func(mat[0], 3, 5);