web-dev-qa-db-fra.com

Manière correcte de passer un tableau à 2 dimensions dans une fonction

J'ai un tableau à 2 dimensions et je le passe dans une fonction pour effectuer certaines opérations. Je voudrais savoir la bonne façon de le faire ...

#define numRows 3
#define numCols 7
#define TotalNum (numRows*numCols)
int arr[numRows][numCols] = {{0,1,2,3,4,5,6}, {7,8,9,10,11,12,13},{14,15,16,17,18,19,20}};

void display(int **p)
{
    printf("\n");
    for (int i = 0; i< numRows;i++)
    {
        for ( int j = 0;j< numCols;j++)
        {
            printf("%i\t",p[i][j]);
        }
        printf("\n");
    }
}

int main() {
    display(arr);
}

Je reçois un message d'erreur:

'display': cannot convert parameter1 from 'int' to 'int*'

Est-ce la bonne façon de passer un tableau à 2 dimensions dans une fonction? Si non, quelle est la bonne manière?

53
lakesh

Vous devriez déclarer votre fonction comme ceci:

void display(int p[][numCols])

Ceci C FAQ explique en détail pourquoi. En résumé, les tableaux se décomposent en pointeurs une fois, cela ne se produit pas de manière récursive. Un tableau de tableaux se décompose en un pointeur vers un tableau, pas en un pointeur vers un pointeur.

69
cnicutar

Si (comme dans votre cas), vous connaissez les dimensions du tableau au moment de la compilation, vous pouvez écrire justvoid display(int p[][numCols]).

Une explication: Vous savez probablement que lorsque vous passez un tableau à une fonction, vous passez en réalité un pointeur au premier membre. En langage C, un tableau 2D est juste un tableau de tableaux. Pour cette raison, vous devez transmettre à la fonction un pointeur sur le premier sous-tableau du tableau 2D. Ainsi, la manière naturelle, est de dire int (*p)[numCols] (cela signifie p est un pointeur sur un tableau de numCols ints). Dans la déclaration de fonction, vous avez le "raccourci" p[], ce qui signifie exactement la même chose que (*p) (mais indique au lecteur que vous passez un pointeur sur un début de tableau et non sur une seule variable)

13
asaelr

Vous faites de la mauvaise façon. Vous pouvez passer un tableau 2D à l'aide d'un pointeur à un tableau ou simplement passer un tableau ou par le biais d'un pointeur unique.

#define numRows 3
#define numCols 7
void display(int (*p)[numcols],int numRows,int numCols)//First method//
void display(int *p,int numRows,int numCols) //Second Method//
void display(int numRows,int numCols,int p[][numCols])  //Third Method
{
    printf("\n");
    for (int i = 0; i < numRows;i++)
    {
        for ( int j = 0; j < numCols;j++)
        {
            printf("%i\t",p[i][j]);
        }
        printf("\n");
    }
}

int main() {
    display(arr,numRows,numCols);
}
6
Varun Chhangani

Il y a plusieurs façons, parfois équivalentes, de le faire. En déclarant un tableau (cf. method_c()), en utilisant un pointeur (cf. method_b()) ou en utilisant un pointeur sur un tableau d'un tableau (cf. method_a()). method_b(), en utilisant un seul pointeur, est légèrement plus difficile à comprendre, car il n’est pas facile d’utiliser l’indexation de tableau standard et nous utilisons donc l’arithmétique de pointeur. method_a() et method_c() sont fondamentalement équivalents, car les tableaux se décomposent de manière non récursive en pointeurs lors de la compilation. Voici un petit programme illustrant les trois méthodes. Nous commençons par initialiser un 2x4- array arr dans une boucle for simple et l’imprimons. Il ressemblera à ceci:

arr:
0 1 2 3
0 1 2 3

Ensuite, nous appelons les trois méthodes. method_a() ajoute 1, method_b() ajoute 2 et method_c() ajoute 3 à tous les éléments. Après chaque appel, nous imprimons à nouveau le tableau arr. Si une fonction a fonctionné correctement, vous la verrez facilement sur la sortie. La taille est arbitraire et peut être réglée via les deux macros ROW et COL. Une dernière remarque, method_c() s'appuie sur un tableau de longueur variable présent depuis C99.

#include <stdio.h>
#include <stdlib.h>

#define ROW 2
#define COL 4

void method_a(int m, int n, int (*ptr_arr)[n]);
void method_b(int m, int n, int *ptr_arr);
void method_c(int m, int n, int arr[][n]);

int main(int argc, char *argv[]) {

    int arr[ROW][COL];

    int i;
    int j;
    for(i = 0; i < ROW; i++) {
        for(j = 0; j < COL; j++) {
            arr[i][j] = j;
        }
    }

    printf("Original array:\n");
    for (i = 0; i < ROW; i++) {
        for(j = 0; j < COL; j++) {
            printf("%d\t", arr[i][j]);
        }
        printf("\n");
    }

    printf("\n\n");

    method_a(ROW, COL, arr);

    printf("method_a() array:\n");
    for (i = 0; i < ROW; i++) {
        for(j = 0; j < COL; j++) {
            printf("%d\t", arr[i][j]);
        }
        printf("\n");
    }

    printf("\n\n");

    printf("method_b() array:\n");
    method_b(ROW, COL, (int *)arr);

    for (i = 0; i < ROW; i++) {
        for(j = 0; j < COL; j++) {
            printf("%d\t", arr[i][j]);
        }
        printf("\n");
    }

    printf("\n\n");

    method_c(ROW, COL, arr);

    printf("method_c() array:\n");
    for (i = 0; i < ROW; i++) {
        for(j = 0; j < COL; j++) {
            printf("%d\t", arr[i][j]);
        }
        printf("\n");
    }

    printf("\n\n");

    return EXIT_SUCCESS;
}

void method_a(int m, int n, int (*ptr_arr)[n])
{
    int i, j;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            ptr_arr[i][j] = j + 1;
        }
    }
}

void method_b(int m, int n, int *ptr_arr)
{
    int i, j;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            /* We need to use pointer arithmetic when indexing. */
            *((ptr_arr + i * n) + j) = j + 2;
        }
    }
    /* The whole function could have also been defined a bit different by taking
     * the i index out of the pointer arithmetic. n alone will then provide our
     * correct offset to the right. This may be a bit easier to understand. Our
     * for-loop would then look like this:
     * for (i = 0; i < m; i++)
     * {
     *     for (j = 0; j < n; j++)
     *     {
     *         *((ptr_arr + n) + j) = j + 2;
     *     }
     *     ptr_arr++;
     * }*/
}

void method_c(int m, int n, int arr[][n])
{
    int i, j;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            arr[i][j] = j + 3;
        }
    }
}
5
lord.garbage

Le déclarer simplement

void display(int (*p)[numCols][numRows]);

Ainsi, votre pointeur p transmet toutes les informations nécessaires et vous pouvez en extraire toutes les dimensions sans répéter numCols et numRows encore et encore.

void display(int (*p)[numCols][numRows])
{
   size_t i, j;

   printf("sizeof array=%zu\n", sizeof *p);
   printf("sizeof array[]=%zu\n", sizeof **p);
   printf("sizeof array[][]=%zu\n", sizeof ***p);

   size_t dim_y = sizeof *p / sizeof **p;
   printf("dim_y = %zu\n", dim_y);

   size_t dim_x = sizeof **p / sizeof ***p;
   printf("dim_x = %zu\n", dim_x);

   for(i=0; i<dim_y; i++) {
      puts("");
      for(j=0; j<dim_x; j++)
         printf(" %6d", (*p)[i][j]);
   }
}

Ceci est particulièrement intéressant si vous utilisez des typedefs (que je n'aime pas d'ailleurs)

 typedef int matrix[5][6];

Dans ce cas, les dimensions ne sont pas visibles dans la signature de la fonction, mais celle-ci conservera les valeurs correctes pour les dimensions.

0
Patrick Schlüter

Vous pouvez modifier la signature de la méthode d'affichage comme suit:

void display(int (*p)[numCols])

Ici, p est un pointeur sur la ligne d'un tableau 2D. Le pointeur n'a besoin que de connaître le nombre de colonnes du tableau. 

En fait, le pointeur doit connaître la taille de chaque ligne. Ceci est très important pour l'arithmétique de pointeur. Ainsi, lorsque vous incrémentez le pointeur, il doit pointer vers la ligne suivante.

Notez ici que p n'est pas un pointeur entier normal. C'est un pointeur entier sur la taille de la mémoire égale à integer_size x columns.

En gros, vous n'avez rien à changer. display(arr) est très bien.

0
Mav55