Passer un tableau 2D à une fonction C++

j'ai une fonction qui je veux prendre, comme 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 quand je le passe dans. Toute modification apportée dans myFunction devrait modifier l'état de anArray . Si Je comprendre correctement, je veux seulement passer comme argument un pointeur vers 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 cela?

234
demandé sur Peter Mortensen 2012-01-07 07:40:31

12 réponses

il y a 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 vers 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);
    
339
répondu shengy 2015-02-25 18:38:23

Taille Fixe

1. Passer par la 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, puisqu'il n'est pas nécessaire de s'inquiéter que l'appelant passe une dimension incorrecte (le compilateur signale en cas d'inadéquation). Cependant, ce n'est pas possible avec les tableaux dynamiques (freestore); cela fonctionne pour automatique ( habituellement stack-living ) tableaux c'est à dire la dimension doit être connu 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 en C de la méthode précédente passe le tableau par pointeur. Ceci ne doit pas être confondu avec le passage par le type de pointeur décayé du tableau (3) , qui est la méthode commune, populaire, bien que moins sûr que celui-ci, mais plus souple. Comme (1) , utilisez cette méthode lorsque toutes les dimensions du tableau est fixe et connu à 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) .

"1519140920 Variable" Taille

ceux-ci sont hérités de C mais sont moins sûrs, le compilateur n'a aucun moyen de vérifier, garantissant que l'appelant passe les dimensions requises. La fonction ne repose que sur ce que l'appelant passe en tant que dimension(s). Ceux-ci sont plus flexibles que ceux ci-dessus puisque 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 [alors qu'en C++ ils peuvent être passés comme référence (1) ]; (2) est en train de passer un pointeur vers le tableau et non le tableau lui-même. Toujours passer un tableau comme-est devient un pointeur-copie Opération qui est facilitée par la nature du tableau de décomposition dans un pointeur .

3. Passer par (valeur) un pointeur vers le type

// 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 recommande pas au-dessus de la syntaxe ci-dessus puisque la syntaxe ci-dessus indique clairement que l'identificateur array est un pointeur unique vers un tableau de 10 entiers, alors que cette syntaxe ressemble à comme 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 rangée simple (i.e. la taille de la colonne, 10 ici) mais le nombre de rangées est inconnu et donc à passer comme un argument. Dans ce cas, il y a une certaine sécurité puisque le compilateur peut signaler quand un pointeur vers un tableau avec une deuxième dimension qui n'est pas égale à 10 est passé. La première dimension est la partie variable et peut être omise. voir ici pour la raison d'être sur la raison pour laquelle seule la première dimension peut être omise.

4. Passer par le 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 y a 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écompose en un pointeur devenant ainsi int **array . Peut-être que c'est juste un repère pour l'appelant que le tableau transmis doit avoir au moins 10 colonnes, même alors, le nombre de lignes est requis. Dans tous les cas, le compilateur ne signale aucune violation de la longueur/taille (il ne vérifie que si le type passé est un pointeur vers un pointeur), ce qui signifie qu'exiger des nombres de lignes et de colonnes comme paramètre a du sens ici.

Note: (4) est l'option la moins sûre car il n'a pratiquement aucun contrôle de type et le plus incommode. 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); comme il peut potentiellement conduire à un comportement non défini en raison de l'aplatissement des réseaux. La bonne façon de passer un tableau dans cette méthode nous amène à la partie incommode, c'est-à-dire que nous avons besoin d'un tableau (de substitution) supplémentaire de pointeurs avec chacun de ses éléments pointant vers la rangée respective du tableau actuel à passer; ce tableau de substitution est alors passé à la fonction (Voir ci-dessous); tout cela pour obtenir le même travail fait que les méthodes ci-dessus qui sont plus sûrs, plus propres et peut-être plus rapide.

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**
}
132
répondu legends2k 2017-05-23 12:02:56

une modification à la première suggestion de shengy, vous pouvez utiliser des gabarits pour faire accepter la fonction un tableau multidimensionnel variable (au lieu de stocker un tableau de pointeurs qui doivent ê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 d'impression sont là pour montrer que les tableaux sont passés par référence (en affichant les adresses des variables)

34
répondu Zrax 2014-07-27 19:21:04

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

alors vous avez les deux tailles de dimension via R et C. Une fonction différente sera créée pour chaque taille de tableau, donc si votre fonction est grande et que vous l'appelez avec une variété de tailles de tableau différentes, cela peut être coûteux. Vous pouvez l'utiliser comme un wrapper sur une fonction comme ceci:

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 comprendre les décalages de l'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);
}
19
répondu Benjamin Lindley 2012-01-07 04:20:20

anArray[10][10] n'est pas un pointeur vers un pointeur, c'est un morceau contigu de mémoire adapté pour stocker 100 valeurs de type double, dont le compilateur sait comment s'adresser parce que vous avez spécifié les dimensions. Vous devez le passer à une fonction comme un 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++ est d'utiliser des std::vector<std::vector<double> > : il est presque aussi efficace, et beaucoup plus pratique.

10
répondu dasblinkenlight 2012-01-07 03:54:05

surpris que personne n'a mentionné cela encore, mais vous pouvez simplement template sur n'importe quoi 2D supportant [][] 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 n'importe quelle infrastructure de données 2D de type" tableau", comme std::vector<std::vector<T>> , ou un type défini par l'utilisateur pour maximiser la réutilisation du code.

9
répondu LemonPi 2014-12-22 18:26:25

Unique dimensions tableau se désintègre à un pointeur de pointeur pointant vers le premier élément du tableau. Alors qu'un tableau 2D se décompose en un pointeur pointant vers la première rangée. Donc, le prototype de fonction devrait être -

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

je préférerais std::vector plutôt que des matrices brutes.

7
répondu Mahesh 2012-01-07 03:54:54

Vous pouvez faire quelque chose comme cela...

#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
7
répondu Sagar Shah 2014-09-20 14:57:13

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 à l'échelle mondiale (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.En utilisant un seul pointeur : Dans cette méthode,nous devons transtypage le tableau 2D en passant à 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;
}`
1
répondu sonorous 2017-06-10 04:41:29

voici un vecteur de vecteurs exemple de matrice

#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
1
répondu edW 2018-07-07 19:21:20

vous pouvez utiliser template facility en C++ pour faire ceci. 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 que vous fournissez, la définition d'une nouvelle fonction est instanciée en utilisant le modèle. so,

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 fonction (Une où col = 3 et une où col = 5).

0
répondu vantony 2015-11-22 18:51:21

nous pouvons utiliser plusieurs façons de passer un tableau 2D à une fonction:

  • en utilisant le pointeur simple nous devons typecast 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;
    }
    
  • en utilisant le pointeur double de cette façon, nous avons aussi composé 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;
   }
0
répondu rashedcs 2018-06-20 11:51:54