Correct way of passing 2 dimensional array into a function

C

C Problem Overview


I have a 2-dimensional array and I am passing it into a function to carry out certain operations. I'd like to know the correct way of doing it...

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

I get an error message:

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

Is this the correct way of passing a 2-dimensional array into a function? If not, what is the correct way?

C Solutions


Solution 1 - C

You should declare your function like this:

void display(int p[][numCols])

This C FAQ thoroughly explains why. The gist of it is that arrays decay into pointers once, it doesn't happen recursively. An array of arrays decays into a pointer to an array, not into a pointer to a pointer.

Solution 2 - C

If (like in your case), you know the dimensions of the array at compilation-time, you can write justvoid display(int p[][numCols]).

Some explanation: You probably know that when you pass an array to a function, you actually pass a pointer to the first member. In C language, 2D array is just an array of arrays. Because of that, you should pass the function a pointer to the first sub-array in the 2D array. So, the natural way, is to say int (*p)[numCols] (that means p is a pointer, to an array of numCols ints). In function declaration, you have the "shortcut" p[], that means exactly the same thing like (*p) (But tells the reader, that you pass a pointer to a beginning of array, and not to just an one variable)

Solution 3 - C

You are doing in wrong way. You can pass 2-d array with the help of pointer to an array, or simply pass an array or through Single pointer.

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

Solution 4 - C

There are several, sometimes equivalent ways of doing this. By declaring an array (cf. method_c()), by using a pointer (cf. method_b()) or by using a pointer to an array of an array (cf. method_a()). method_b(), using a single pointer, is slightly more difficult to get right since it is not easy to use standard array indexing and hence, we use pointer arithmetic. method_a() and method_c() are basically equivalent since arrays decay non-recursively to pointers during compilation. Here is a little program illustrating all three methods. We first initialize a 2x4-array arr in a simple for loop and print it. It will look like this:

arr:
0 1 2 3
0 1 2 3

Afterwards we call all three methods. method_a() adds 1, method_b() adds 2 and method_c() adds 3 to all elements. After each call we print out the array arr again. If a function worked correctly you will easily see it on the output. Size is arbitrary and can be regulated via the two macros ROW and COL. One last note, method_c() relies on variable-length array present since 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;
        }
    }
}

Solution 5 - C

Declare it simply

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

This way your p pointer conveys all necessary informations and you can extract all the dimensions from it without repeating numCols and numRows over and over.

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

This is particularly interesting if you use typedefs (which I don't like btw)

 typedef int matrix[5][6];

In that case the dimensions are not visible in the signature of the function but the function will still have the correct values for the dimensions.

Solution 6 - C

You can change the signature of display method as follows:

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

Here, p is a pointer to the row of a 2D array. The pointer only needs to know the number of columns in the array.

Actually speaking, pointer need to know the size of each row. This is very important for pointer arithmetic. So that when you increment the pointer, the pointer must point to next row.

Note here, p is not a normal integer pointer. It's a integer pointer to the memory size equal to integer_size x columns.

In main you don't need to change anything. display(arr) is just fine.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionlakshmenView Question on Stackoverflow
Solution 1 - CcnicutarView Answer on Stackoverflow
Solution 2 - CasaelrView Answer on Stackoverflow
Solution 3 - CVarun ChhanganiView Answer on Stackoverflow
Solution 4 - Clord.garbageView Answer on Stackoverflow
Solution 5 - CPatrick SchlüterView Answer on Stackoverflow
Solution 6 - CMav55View Answer on Stackoverflow