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


Answers

Le problème est que

int a[4][4];

sera effectivement stocké dans une mémoire physiquement continue. Donc, 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 va accéder à 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);
}

Donc, la disposition de la mémoire ne change pas, mais la façon d'y accéder. 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 stockage nécessaire lorsque le tableau est déclaré, mais n'est plus nécessaire pour calculer le décalage par la suite.

Question
#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?




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




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



Voici une version qui fonctionne tous les deux, 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 la 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;
}

Alternativement, vous pouvez utiliser des conteneurs STL imbriqués - c'est-à vector< vector<int> > 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 appelez-le comme

print(4, 4, a);

Comme Robert l'a souligné dans les commentaires, le premier extrait implique un comportement non défini. Cependant, en supposant que l'arithmétique du pointeur produise toujours un pointeur même si 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 dans la norme, c'est-à-dire standard laisse quelque chose inutilement indéfini.

Pour autant que je sache, remplacer

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

avec

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

le rendra légal C.




#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 va compiler éditer: quelqu'un a déjà posté cette solution ma mauvaise




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 lancer dans cela maintenant. Peut-être quelqu'un d'autre?

Je ne suis évidemment pas choqué par le même point que d'autres affiches dans 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 changer le pointeur initial (en fait, cela ne peut pas être fait car c'est une constante). @ | V | lad répondre à ceci en définissant une ou deux dimensions à une constante fixe, mais en passant s1 et s2 deviennent inutiles.

Tout dépend de ce que vous voulez vraiment faire. L'impression est-elle une fonction d'impression de tableau à usage général ou spécialisée pour certains types de tableaux?