Passer un tableau 2D à une fonction C ++

J’ai une fonction que je veux prendre, en paramètre, un tableau 2D de taille variable.

Jusqu’à présent, 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 veux pas copier le tableau lorsque je le transmets. Toute modification apscope à myFunction devrait modifier l’état d’un 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 tailles différentes. Ainsi, par exemple, [10][10] et [5][5] . Comment puis-je faire ceci?

    Il existe trois manières de transmettre un tableau 2D à une fonction:

    1. Le paramètre est un tableau 2D

       int array[10][10]; void passFunc(int a[][10]) { // ... } passFunc(array); 
    2. 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); 
    3. 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); 

    Taille fixe

    1. passer par référence

     template  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 est inutile de se soucier du fait que l'appelant transmette une dimension incorrecte (indicateurs du compilateur en cas de discordance). Cependant, ce n'est pas possible avec les tableaux dynamics (freestore); cela fonctionne uniquement 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 transmet le tableau par pointeur. Cela ne doit pas être confondu avec le passage par le type de pointeur décomposé du tableau (3) , qui est la méthode commune la plus populaire, mais 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 passée à process_2d_array_pointer(&a) et non à l'adresse du premier élément par decay 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 requirejses. La fonction se limite à ce que l'appelant passe en tant que 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 ne faut pas oublier que passer directement un tableau à une fonction dans C [alors qu’en C ++ ils peuvent être passés en tant que référence (1) ]; (2) passe un pointeur sur le tableau et non sur le tableau lui-même. Passer toujours un tableau tel quel devient une opération de copie de pointeur facilitée par la nature de la désintégration du tableau en pointeur .

    3. Passer par (valeur) un pointeur sur 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] soit autorisé, je ne le recommanderais pas avec la syntaxe ci-dessus car la syntaxe ci-dessus indique clairement que le array identifiants est un simple pointeur vers un tableau de 10 entiers. 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.-à-d. 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 marquer le passage d'un pointeur vers un tableau de deuxième dimension non égal à 10. La première dimension est la partie variable et peut être omise. Voir ici la raison pour laquelle seule la première dimension peut être omise.

    4. Passer par un pointeur sur 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 la même que int **array . Dans cette syntaxe, le [10] est ignoré car il se désintègre en un pointeur, devenant ainsi un int **array . Peut-être est-ce juste un signal pour l'appelant que le tableau passé doit avoir au moins 10 colonnes, même alors le nombre de lignes est requirejs. Dans tous les cas, le compilateur ne signale aucune violation de longueur / taille (il vérifie uniquement si le type passé est un pointeur sur le pointeur).

    Remarque: (4) est l’option la moins sûre car elle n’a pratiquement aucune vérification de type et est la moins pratique. On ne peut pas légitimement passer un tableau 2D à cette fonction; C-FAQ condamne le contournement habituel de faire int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); 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 du tableau. La bonne manière de passer un tableau dans cette méthode nous amène à la partie dérangeante, c’est-à-dire qu’il nous faut un tableau additionnel de pointeurs avec chacun de ses éléments pointant vers la ligne respective du tableau réel à transmettre; 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 de pilote pour tester les fonctions ci-dessus:

     #include  // 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 multidimensionnelle (au lieu de stocker un tableau de pointeurs à gérer et à supprimer):

     template  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 void myFunction(double (&myArray)[R][C]) { myArray[x][y] = 5; etc... } 

    Vous disposez alors des deux dimensions 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 différentes tailles de tableau, cela peut être coûteux. Vous pouvez l’utiliser comme un wrapper 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 void myFunction(double (&myArray)[R][C]) { myFunction(*myArray, R, C); } 

    anArray[10][10] n’est pas un pointeur sur un pointeur, c’est un bloc de mémoire contigu qui permet de stocker 100 valeurs de type double, que le compilateur sait traiter car vous avez spécifié les dimensions. Vous devez le transmettre à une fonction en tant que tableau. Vous pouvez omettre la taille de la dimension initiale, comme suit:

     void f(double p[][10]) { } 

    Cependant, cela ne vous laissera pas passer des tableaux avec la dernière dimension autre que dix.

    La meilleure solution en C ++ consiste à utiliser std::vector > : il est presque aussi efficace et nettement plus pratique.

    Surpris que personne ne l’ait encore mentionné, vous pouvez simplement créer des modèles sur tout support 2D prenant en charge la sémantique [].

     template  void myFunction(TwoD& myArray){ myArray[x][y] = 5; etc... } // call with double anArray[10][10]; myFunction(anArray); 

    Il fonctionne avec n’importe quelle structure de données 2D “array-like”, telle que std::vector> , ou un type défini par l’utilisateur pour maximiser la réutilisation du code.

    Un tableau à une seule dimension se désintègre en un pointeur pointant sur le premier élément du tableau. Alors qu’un tableau 2D se désintègre en un pointeur pointant vers la première ligne. Donc, le prototype de fonction devrait être –

     void myFunction(double (*myArray) [10]); 

    Je préférerais std::vector sur les tableaux bruts.

    Vous pouvez faire quelque chose comme ça …

     #include using namespace std; //for changing values in 2D array void myFunc(double *a,int rows,int cols){ for(int i=0;i 

    Votre sortie sera comme suit ...

     11.5 12.5 13.5 14.5 

    Une chose importante pour le passage de tableaux multidimensionnels est la suivante:

    • First array dimension n’a pas besoin d’être spécifiée.
    • Second(any any further)dimension doit être spécifiée.

    1.Quand seule la deuxième dimension est disponible globalement (soit en tant que macro, soit en tant que 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 convertir le tableau 2D lors du passage à la fonction.

     `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; }` 

    Voici un vecteur de la masortingce de vecteurs exemple

     #include  #include  using namespace std; typedef vector< vector > Masortingx; void print(Masortingx& m) { int M=m.size(); int N=m[0].size(); for(int i=0; i(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 

    Pour ce faire, vous pouvez utiliser la fonctionnalité de modèle dans C ++. J’ai fait quelque chose comme ça:

     template T process(T a[][col], size_t row) { ... } 

    Le problème avec cette approche est que pour chaque valeur de col que vous fournissez, 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 deux définitions de fonction (une où col = 3 et une où col = 5).

    Nous pouvons utiliser plusieurs manières de transmettre un tableau 2D à une fonction:

    • En utilisant un seul pointeur, nous devons convertir le tableau 2D.

       #include using namespace std; void func(int *arr, int m, int n) { for (int i=0; i 
    • Utiliser un double pointeur De cette façon, nous avons également mis en page le tableau 2d

      #include using namespace std; void func(int **arr, int row, int col) { for (int i=0; i>row>>colum; int** arr = new int*[row]; for(int i=0; i>arr[i][j]; } } func(arr, row, colum); return 0; }