web-dev-qa-db-fra.com

Comment passer un tableau multidimensionnel à une fonction en C et C++

#include<stdio.h>
void print(int *arr[], int s1, int s2) {
    int i, j;
    for(i = 0; i<s1; i++)
        for(j = 0; j<s2; j++)
            printf("%d, ", *((arr+i)+j));
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

Cela fonctionne en C, mais pas en C++.

erreur:

cannot convert `int (*)[4]' to `int**' for argument `1' to 
`void print(int**, int, int)'

Pourquoi ça ne marche pas en C++? Quel changement faut-il faire?

37
Moeb

Le problème est que 

int a[4][4];

sera effectivement stocké dans une mémoire physiquement continue. Ainsi, pour accéder à une partie arbitraire de votre tableau 4x4, la fonction "print" doit connaître les dimensions du tableau. Par exemple, le petit morceau de code suivant accédera à la même partie de la mémoire de deux manières différentes. 

#include <iostream>

void print(int a[][4]){
    for (int i = 0; i <4; i++){
        for (int j = 0; j < 4; j++){
            //accessing as 4x4 array
            std::cout << a[i][j] <<std::endl;        

            //accessing corresponding to the physical layout in memory
            std::cout <<  *(*(a)+ i*4 + j) << std::endl;  

        }
    }
}

int main(){
    int a[4][4];

    //populating the array with the corresponding indices from 0 to 15
    int m = 0;
    for (int i = 0; i<4; i++){
        for (int j= 0; j < 4; j++){
            a[i][j] =  m;
            m++;
        }
    }
    print(a);
}

Ainsi, la disposition de la mémoire ne change pas, mais la manière d’accéder change. Il peut être visualisé comme un damier.

   0  1  2  3
  ----------
0| 1  2  3  4
1| 5  6  7  8
2| 9 10 11 12
3|13 14 15 16

Mais la vraie mémoire physique ressemble à ceci. 

0*4+0 0*4+1 0*4+2 0*4+3 1*4+0 1*4+1 1*4+2 1*4+3 2*4+1   etc.
-----------------------------------------------------
1      2       3    4     5     6      7     8     9    etc.

En c ++, les données d'un tableau sont stockées ligne par ligne et la longueur d'une ligne (dans ce cas 4) est toujours nécessaire pour obtenir le décalage de mémoire approprié pour la ligne suivante. Le premier indice indique donc uniquement la quantité de mémoire nécessaire lors de la déclaration du tableau, mais n'est plus nécessaire pour calculer l'offset par la suite. 

15
Lucas
#include<stdio.h>
void print(int arr[][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

Cela fonctionnera, où par travail j'entends compiler. @AndreyT a expliqué pourquoi votre version ne fonctionnait pas déjà.

Voici comment vous devriez passer un tableau 2d.

Pour plus de clarté, vous pouvez également spécifier les deux tailles dans la déclaration de fonction:

#include<stdio.h>
void print(int arr[4][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

Les deux vont marcher.

Vous devez également modifier *((arr+i)+j) en a[i][j] (de préférence) ou *(*(arr+i)+j) si vous souhaitez accéder à l'élément jth de la ligne i.

8
IVlad

Voici une version qui fonctionne à la fois, mais théoriquement invalide (voir ci-dessous) C90 et C++ 98:

#include <stdio.h>

static void print(int *arr, size_t s1, size_t s2)
{
    size_t i, j;
    printf("\n");
    for(i = 0; i < s1; i++) {
        for(j = 0; j < s2; j++) {
            printf("%d, ", arr[i * s2 + j]);
        }
    }
    printf("\n");
}

int main(void) {
    int a[4][4] = {{0}};
    print(a[0], 4, 4);
    return 0;
}

Une version C++ utilisant des modèles (adaptée de Réponse de Notinlist ) pourrait ressembler à ceci:

#include <iostream>
#include <cstring>

using namespace std;

template <size_t N, size_t M>
struct IntMatrix
{
    int data[N][M];
    IntMatrix() { memset(data, 0, sizeof data); }
};

template <size_t N, size_t M>
ostream& operator<<(ostream& out, const IntMatrix<N,M>& m)
{
    out << "\n";
    for(size_t i = 0; i < N; i++) {
        for(size_t j = 0; j < M; j++) {
            out << m.data[i][j] << ", ";
        }
    }
    out << "\n";
    return out;
}

int main()
{
    IntMatrix<4,4> a;
    cout << a;
    return 0;
}

Sinon, vous pouvez utiliser des conteneurs STL imbriqués, c'est-à-dire vector< vector<int> >, au lieu d'un tableau simple.

Avec C99, vous pourriez faire

static void print(size_t s1, size_t s2, int arr[s1][s2]) {
    printf("\n");
    for(size_t i = 0; i < s1; i++) {
        for(size_t j = 0; j < s2; j++) {
            printf("%d, ", arr[i][j]);
        }
    }
    printf("\n");
}

et l'appeler comme

print(4, 4, a);

Comme Robert l'a souligné dans les commentaires, le premier extrait implique en réalité un comportement indéfini. Cependant, en supposant que l'arithmétique de pointeur aboutisse toujours à un pointeur même lorsqu'un comportement indéfini est impliqué (et ne fait pas exploser votre ordinateur), il n'y a qu'un seul résultat possible en raison d'autres restrictions de la norme, par la norme laisse quelque chose d'inutilement indéfini.

Pour autant que je sache, en remplaçant

print(a[0], 4, 4);

avec

union m2f { int multi[4][4]; int flat[16]; } *foo = (union m2f *)&a;
print(foo->flat, 4, 4);

va le rendre légal C.

6
Christoph

Vous pouvez utiliser int** à la place. C'est beaucoup plus flexible:

#include <stdio.h>
#include <stdlib.h>
void print(int **a, int numRows, int numCols )
{
  int row, col ;
  for( int row = 0; row < numRows; row++ )
  {
    for( int col = 0; col < numCols ; col++ )
    {
      printf("%5d, ", a[row][col]);
    }
    puts("");
  }
}

int main()
{
  int numRows = 16 ;
  int numCols = 5 ;
  int **a ;

  // a will be a 2d array with numRows rows and numCols cols

  // allocate an "array of arrays" of int
  a = (int**)malloc( numRows* sizeof(int*) ) ;

  // each entry in the array of arrays of int
  // isn't allocated yet, so allocate it
  for( int row = 0 ; row < numRows ; row++ )
  {
    // Allocate an array of int's, at each
    // entry in the "array of arrays"
    a[row] = (int*)malloc( numCols*sizeof(int) ) ;
  }

  int count = 1 ;
  for( int row = 0 ; row < numRows ; row++ )
  {
    for( int col = 0 ; col < numCols ; col++ )
    {
      a[row][col] = count++ ;
    }
  }

  print( a, numRows, numCols );
}

Autre chose qui pourrait vous intéresser est une structure du type D3DMATRIX :

 typedef struct _D3DMATRIX {
 syndicat {
 struct {
 float _11, _12, _13, _14; 
 float _21, _22, _23, _24; 
 float _31, _32, _33, _34; 
 float _41, _42, _43, _44; 

 }; 
 float m [4] [4]; 
 }; 
} D3DMATRIX; 

 D3DMATRIX myMatrix; 

La bonne chose à propos de ce petit détail est que vous pouvez utiliser les deux myMatrix.m[0][0] (pour accéder au premier élément), ou vous pouvez également utiliser myMatrix._11 pour accéder également au même élément. Le union est le secret.

4
bobobobo
#include<cstdio>
template <size_t N, size_t M>
struct DataHolder
{
    int data[N][M];
    DataHolder()
    {
       for(int i=0; i<N; ++i)
           for(int j=0; j<M; ++j)
               data[i][j] = 0;
    }
};

template <size_t N, size_t M>
void print(const DataHolder<N,M>& dataHolder) {
    printf("\n");
    for(int i = 0; i<N; i++) {
        for(int j = 0; j<M; j++) {
            printf("%d, ", dataHolder.data[i][j]);
        }
    }
    printf("\n");
}

int main() {
    DataHolder<4,4> a;
    print(a);
}
2
Notinlist

En plus d'utiliser des tableaux de longueur variable dans C99, vous ne pouvez pas vraiment portably écrire une fonction pour accepter un tableau multidimensionnel si la taille des tableaux n'est pas connue au moment de la compilation, Voir Question 6.19 de le C-FAQ . La meilleure façon de gérer cela consiste à simuler des tableaux multidimensionnels à l'aide de mémoire allouée dynamiquement. Question 6.16 explique très bien les détails de cette opération.

1
Robert Gamble

Les tableaux multidimensionnels sont des blocs de mémoire continus. Donc, vous pouvez le faire de cette façon:

#include <stdio.h>

void pa(const int *a, int y, int x)
{
    int i, j;
    for (i=0;i<y;i++)
    {
        for (j=0;j<x;j++)
            printf("%i", *(a+j+i*x));
        printf("\n");
    }
}

int main()
{
    int a[4][3] = { {1,2,3},
                    {4,5,6},
                    {4,5,6},
                    {7,8,9} };

    pa(a[0], 4, 3);

    return 0;
}

Cela fonctionne aussi en C++;

0
it-west.net

La première chose à faire est d'obtenir les bons types. Si les règles de C++ sont identiques à celles de C en ce qui concerne les types de tableaux (j'en suis quasiment sûr), alors la déclaration est donnée.

int a[4][4];

l'expression a a le type int [4][4], qui est implicitement converti ("decays") en un type de pointeur de int (*)[4] (pointeur sur un tableau de 4 éléments d'int) lorsqu'il est passé à print, vous devez donc changer print

void print(int (*arr)[4], int s1, int s2)
{
  int i, j;        
  for(i = 0; i<s1; i++)        
    for(j = 0; j<s2; j++)        
      printf("%d, ", arr[i][j]);        
}        

L'expression arr[i] supprime implicitement arr, vous n'avez donc pas besoin de jouer avec une déréférencement explicite. 

L'inconvénient est que print ne peut gérer que les tableaux Nx4 d'int; si vous souhaitez gérer d'autres tailles de tableau, vous devrez adopter une approche différente. 

Une chose que vous pouvez faire est, au lieu de passer le tableau, de transmettre l'adresse du premier élément et de laisser print calculer manuellement les décalages, comme suit:

int main() {                    
  int a[4][4] = {{0}};                    
  print(&a[0][0],4,4);  // note how a is being passed                  
}  

void print(int *arr, int s1, int s2)  // note that arr is a simple int *
{
  int i, j;
  for (i = 0; i < s1; i++)
    for (j = 0; j < s2; j++)
      printf("%d, ", arr[i * s2 + j]);
}
0
John Bode
#include<stdio.h>
void print(int (*arr)[4], int s1, int s2) {
    int i, j;
    for(i = 0; i<s1; i++)
        for(j = 0; j<s2; j++)
            printf("%d, ", arr[i][j]);
}

int main() {
    int a[4][4] = {{6}};
    print(a,4,4);
}

cela compilera edit: quelqu'un a déjà posté cette solution my bad

0
devmabbott

Réponse courte, vous pouvez changer le programme comme suit

void print(int arr[], int s1, int s2) {
...
printf("%d,", *(a+i + s2*j));
...
print((int*)a,4,4);

Cela nécessiterait une meilleure réponse expliquant les différences entre l'arithmétique de pointeur et de pointeur et les tableaux en C et C++. Je ne vais pas me lancer dans cela maintenant. Peut-être quelqu'un d'autre?

Je ne suis évidemment pas choqué par le même argument que d'autres affiches de votre code. Ce qui me dérange le plus dans l’entête de la fonction d’impression, c’est que vous utilisez une double indirection pour un tableau où vous n’avez pas l’intention de revenir au pointeur initial (en fait, cela ne peut pas être fait car c’est une constante). @ | V | lad répondez en réglant une ou deux dimensions sur une constante fixe, mais si vous passez s1 et s2, cela devient inutile. 

Tout dépend de ce que vous voulez vraiment faire. Est-ce que print est une fonction d’impression de matrice à usage général ou spécialisée pour certains types de matrice?

0
kriss