web-dev-qa-db-fra.com

Passer un tableau 2D à une fonction C ++

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?

281
RogerDarwin

Il existe trois façons de passer 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);
    
384
shengy

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 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).

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 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**
}
153
legends2k

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)

40
Zrax

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);
}
20
Benjamin Lindley

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.

16
LemonPi

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.

10
dasblinkenlight

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
8
Sagar Shah

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.

8
Mahesh

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
4
edW

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;
   }
2
rashedcs

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

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);
}
0
Purusharth Verma

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).

0
vantony

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);