J'ai une fonction que je veux prendre, en tant que paramètre, un tableau 2D de taille variable.
Jusqu'ici j'ai ceci:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
Et j'ai déclaré un tableau ailleurs dans mon code:
double anArray[10][10];
Cependant, appeler myFunction(anArray)
me donne une erreur.
Je ne souhaite pas copier le tableau lorsque je le transmettrai. Toute modification apportée dans myFunction
devrait modifier l'état de anArray
. Si je comprends bien, je veux seulement passer en argument un pointeur sur un tableau 2D. La fonction doit également accepter des tableaux de différentes tailles. Ainsi, par exemple, [10][10]
et [5][5]
. Comment puis-je faire ceci?
Il existe trois façons de passer un tableau 2D à une fonction:
Le paramètre est un tableau 2D
int array[10][10];
void passFunc(int a[][10])
{
// ...
}
passFunc(array);
Le paramètre est un tableau contenant des pointeurs
int *array[10];
for(int i = 0; i < 10; i++)
array[i] = new int[10];
void passFunc(int *a[10]) //Array containing pointers
{
// ...
}
passFunc(array);
Le paramètre est un pointeur sur un pointeur
int **array;
array = new int *[10];
for(int i = 0; i <10; i++)
array[i] = new int[10];
void passFunc(int **a)
{
// ...
}
passFunc(array);
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 la solution la plus sûre, car il n'est pas à craindre que l'appelant transmette une dimension incorrecte (les indicateurs du compilateur ne correspondent pas). Cependant, cela n’est pas possible avec les tableaux dynamiques (freestore); cela fonctionne pour les tableaux automatiques ( généralement en pile ), 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 désintégré du tableau (3) , qui est la méthode populaire la plus répandue, bien que moins sûre 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 passée process_2d_array_pointer(&a)
et non l'adresse du premier élément par decay process_2d_array_pointer(a)
.
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 transmet les dimensions requises. La fonction se base uniquement sur ce que l'appelant transmet en tant que dimension (s). Celles-ci sont plus souples que celles ci-dessus, car des tableaux de différentes longueurs peuvent leur être transmis invariablement.
Il faut se rappeler qu’il n’existe pas de passer directement un tableau à une fonction en C [alors qu’en C++, il peut être passé en référence (1) ]; (2) transmet un pointeur au tableau et non au tableau lui-même. Passer toujours un tableau en l'état devient une opération de copie de pointeur qui est facilitée par la nature du tableau se décomposant en un pointeur .
3. Passer (valeur) un pointeur sur le type en décomposition
// 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]
soit autorisé, je ne le recommanderais pas pour la syntaxe ci-dessus, car il indique clairement que l'identificateur array
est un pointeur unique sur un tableau de 10 entiers, tandis que cette syntaxe ressemble à un tableau 2D, mais correspond au même pointeur sur un tableau de 10 entiers. Nous connaissons ici 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 tant qu'argument. Dans ce cas, il y a une certaine sécurité car le compilateur peut signaler lorsqu'un pointeur sur un tableau avec une deuxième dimension différente de 10 est passé. La première dimension est la partie variable et peut être omise. Voir ici pour la raison pourquoi seule la première dimension peut être omise.
4. Passer par un pointeur vers 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é car il se décompose en un pointeur pour devenir ainsi int **array
. Peut-être est-ce juste un signal pour 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 n'indique aucune violation de longueur/taille (il vérifie uniquement si le type transmis est un pointeur à un pointeur), nécessitant donc à la fois le nombre de lignes et de colonnes en tant que paramètre.
Remarque: (4) est l'option la moins sûre car elle n'a pratiquement pas de vérification de type et le plus incommode. On ne peut légitimement pas passer un tableau 2D à cette fonction; C-FAQ condamne la solution habituelle consistant à faire int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
comme cela peut potentiellement conduire à un comportement indéfini en raison de l'aplatissement de la matrice. La bonne façon de passer un tableau dans cette méthode nous amène à la partie incommode, c’est-à-dire qu’il nous faut un tableau supplémentaire (de substitution) de pointeurs, chacun de ses éléments pointant vers la rangée respective du tableau à transmettre; ce substitut est ensuite passé à la fonction (voir ci-dessous); tout cela pour obtenir 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 de 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**
}
Une modification de la première suggestion de shengy, vous pouvez utiliser des modèles pour que la fonction accepte une variable de tableau multidimensionnel (au lieu de stocker un tableau de pointeurs devant être gérés et supprimés):
template <size_t size_x, size_t size_y>
void func(double (&arr)[size_x][size_y])
{
printf("%p\n", &arr);
}
int main()
{
double a1[10][10];
double a2[5][5];
printf("%p\n%p\n\n", &a1, &a2);
func(a1);
func(a2);
return 0;
}
Les instructions print sont là pour montrer que les tableaux sont passés par référence (en affichant les adresses des variables)
Vous pouvez créer un modèle de fonction comme ceci:
template<int R, int C>
void myFunction(double (&myArray)[R][C])
{
myArray[x][y] = 5;
etc...
}
Ensuite, vous avez les deux tailles de dimension via R et C. Une fonction différente sera créée pour chaque taille de tableau. Par conséquent, si votre fonction est volumineuse et que vous l'appelez avec une variété de tailles de tableaux différentes, cela peut être coûteux. Vous pouvez toutefois l'utiliser comme une enveloppe sur une fonction comme celle-ci:
void myFunction(double * arr, int R, int C)
{
arr[x * C + y] = 5;
etc...
}
Il traite le tableau comme une dimension et utilise l'arithmétique pour déterminer les décalages des index. Dans ce cas, vous définiriez le modèle comme ceci:
template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
myFunction(*myArray, R, C);
}
Surpris que personne ne l’ait encore mentionné, mais vous pouvez simplement modéliser sur tout élément 2D prenant en charge la sémantique [] [].
template <typename TwoD>
void myFunction(TwoD& myArray){
myArray[x][y] = 5;
etc...
}
// call with
double anArray[10][10];
myFunction(anArray);
Il fonctionne avec toute structure de données 2D "semblable à un tableau", telle que std::vector<std::vector<T>>
, ou un type défini par l'utilisateur pour optimiser la réutilisation du code.
anArray[10][10]
n'est pas un pointeur sur un pointeur, c'est un bloc de mémoire contigu qui convient au stockage de 100 valeurs de type double que le compilateur sait comment adresser car vous avez spécifié les dimensions. Vous devez le transmettre à une fonction sous forme de tableau. Vous pouvez omettre la taille de la dimension initiale, comme suit:
void f(double p[][10]) {
}
Cependant, cela ne vous laissera pas passer de tableaux avec la dernière dimension autre que dix.
La meilleure solution en C++ consiste à utiliser std::vector<std::vector<double> >
: il est presque aussi efficace et nettement plus pratique.
Vous pouvez faire quelque chose comme ça ...
#include<iostream>
using namespace std;
//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
*(a+ i*rows + j)+=10.0;
}
}
}
//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
cout<<"Printing your array...\n";
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
cout<<*(a+ i*rows + j)<<" ";
}
cout<<"\n";
}
}
int main(){
//declare and initialize your array
double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};
//the 1st argument is the address of the first row i.e
//the first 1D array
//the 2nd argument is the no of rows of your array
//the 3rd argument is the no of columns of your array
myFunc(a[0],2,2);
//same way as myFunc
printArray(a[0],2,2);
return 0;
}
Votre sortie sera comme suit ...
11.5 12.5
13.5 14.5
Un tableau à une dimension se désintègre en un pointeur pointeur pointant vers le premier élément du tableau. Tandis qu'un tableau 2D se décompose en un pointeur pointant sur la première ligne. Donc, le prototype de fonction devrait être -
void myFunction(double (*myArray) [10]);
Je préférerais std::vector
plutôt que des tableaux bruts.
Voici un exemple de matrice de vecteur de vecteurs
#include <iostream>
#include <vector>
using namespace std;
typedef vector< vector<int> > Matrix;
void print(Matrix& m)
{
int M=m.size();
int N=m[0].size();
for(int i=0; i<M; i++) {
for(int j=0; j<N; j++)
cout << m[i][j] << " ";
cout << endl;
}
cout << endl;
}
int main()
{
Matrix m = { {1,2,3,4},
{5,6,7,8},
{9,1,2,3} };
print(m);
//To initialize a 3 x 4 matrix with 0:
Matrix n( 3,vector<int>(4,0));
print(n);
return 0;
}
sortie:
1 2 3 4
5 6 7 8
9 1 2 3
0 0 0 0
0 0 0 0
0 0 0 0
Nous pouvons utiliser plusieurs méthodes pour transmettre un tableau 2D à une fonction:
tilisation d'un seul pointeur nous devons transtyper le tableau 2D.
#include<bits/stdc++.h>
using namespace std;
void func(int *arr, int m, int n)
{
for (int i=0; i<m; i++)
{
for (int j=0; j<n; j++)
{
cout<<*((arr+i*n) + j)<<" ";
}
cout<<endl;
}
}
int main()
{
int m = 3, n = 3;
int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
func((int *)arr, m, n);
return 0;
}
tilisation du double pointeur De cette façon, nous avons également converti le tableau 2d
#include<bits/stdc++.h>
using namespace std;
void func(int **arr, int row, int col)
{
for (int i=0; i<row; i++)
{
for(int j=0 ; j<col; j++)
{
cout<<arr[i][j]<<" ";
}
printf("\n");
}
}
int main()
{
int row, colum;
cin>>row>>colum;
int** arr = new int*[row];
for(int i=0; i<row; i++)
{
arr[i] = new int[colum];
}
for(int i=0; i<row; i++)
{
for(int j=0; j<colum; j++)
{
cin>>arr[i][j];
}
}
func(arr, row, colum);
return 0;
}
Une chose importante pour passer des tableaux multidimensionnels est:
First array dimension
n'a pas besoin d'être spécifié.Second(any any further)dimension
doit être spécifié.1.Lorsque seule la deuxième dimension est disponible globalement (sous forme de macro ou de constante globale)
`const int N = 3;
`void print(int arr[][N], int m)
{
int i, j;
for (i = 0; i < m; i++)
for (j = 0; j < N; j++)
printf("%d ", arr[i][j]);
}`
int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
print(arr, 3);
return 0;
}`
2.Utilisation d'un seul pointeur: Dans cette méthode, nous devons transtyper le tableau 2D lors du passage à function.
`void print(int *arr, int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
printf("%d ", *((arr+i*n) + j));
}
`int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int m = 3, n = 3;
// We can also use "print(&arr[0][0], m, n);"
print((int *)arr, m, n);
return 0;
}`
Dans le cas où vous souhaitez transmettre un tableau à 2 dimensions dynamique à une fonction, l'utilisation de pointeurs peut s'avérer utile.
void func1(int *arr, int n, int m){
...
int i_j_the_element = arr[i * m + j]; // use the idiom of i * m + j for arr[i][j]
...
}
void func2(){
...
int arr[n][m];
...
func1(&(arr[0][0]), n, m);
}
Pour ce faire, vous pouvez utiliser la fonctionnalité de modèle en C++. J'ai fait quelque chose comme ça:
template<typename T, size_t col>
T process(T a[][col], size_t row) {
...
}
le problème avec cette approche est que pour chaque valeur de col fournie, une nouvelle définition de fonction est instanciée à l'aide du modèle. alors,
int some_mat[3][3], another_mat[4,5];
process(some_mat, 3);
process(another_mat, 4);
instancie le modèle deux fois pour produire 2 définitions de fonctions (une où col = 3 et une où col = 5).
Si vous voulez passer int a[2][3]
à void func(int** pp)
, vous avez besoin des étapes auxiliaires suivantes.
int a[2][3];
int* p[2] = {a[0],a[1]};
int** pp = p;
func(pp);
Comme le premier [2]
peut être spécifié implicitement, il peut être simplifié davantage comme.
int a[][3];
int* p[] = {a[0],a[1]};
int** pp = p;
func(pp);