-3

For example, I want to pass a 2-dimensional integer array to a function in C. Should the function be defined as

void foo(int**,int m,int n);

or

void foo(int[][],int m,int n);

I get an error in the first prototype declaration, but I get a warning "incompatible pointer type" while compiling with GCC. Please explain how to pass a 2D array. The main method is as given

int main()  
{  
    int m,n;  
    scanf("%d%d",&m,&n); 
    int a[m][n];  
    foo(a,m,n);  
    return 0;  
}  
  • 1
    Pointer are not arrays, and arrays are not pointers. Read this http://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays – StoryTeller - Unslander Monica Mar 26 '17 at 11:21
  • The answer to question "should the function be defined as X or Y" is "no" by using common boolean logic. Neither is correct for a 2-dimensional array. – Antti Haapala -- Слава Україні Mar 26 '17 at 11:23
  • ... and what is correct in your case depends a lot on what you're calling this function with. – Antti Haapala -- Слава Україні Mar 26 '17 at 11:24
  • 1
    The first is legal C; the second is *not*. What you're *passing* to the former (the one I suspect you get your warning from) is important, and said-code is inconveniently not included in your question. Passing a *real* 2D array (an array of arrays) requires all dimensions but the most-superior be known when forming the formal parameter of your function, either by constant or by variable-length-array (VLA) semantics. – WhozCraig Mar 26 '17 at 11:27
  • Sir, @AnttiHaapala can you please give the prototype of the correctly having 2-D array as formal parameters? –  Mar 26 '17 at 11:27
  • 2
    @AdvaithHL no, it doesn't work like that. You're the one who needs to provide code first here. You said you've got code and you have warnings or compile-time errors being emitted, then it is your responsibility to provide a [MCVE](/help/mcve). – Antti Haapala -- Слава Україні Mar 26 '17 at 11:30
  • Possible duplicate of [Difference between double pointer and array of pointers](http://stackoverflow.com/questions/33491687/difference-between-double-pointer-and-array-of-pointers) – David Hoelzer Mar 26 '17 at 12:26

3 Answers3

2

The both function declarations

void foo(int**,int m,int n);

and

void foo(int[][],int m,int n);

are wrong. A declaration of a parameter having an array type is adjusted to pointer to the element type. So if you have a declaration of a parameter having a two-dimensional array type like this

int a[M][N]

then it is adjusted as

int ( *p )[N}

The value of N must be known for the function definition. If N is a compile-time constant declared for example like

#define N 10

then the function declaration can look like

void foo( int[][N], int );

or

void foo( int[][N], size_t );

where the second parameter specifies the number of "rows". The number of "columns" is known because as I said it is a compile-time constant.

If N is calculated at run-time and the compiler supports Variable Length Arrays then you should declare the function like

void foo( int, int, int[*][*] );

Or instead of the type int for the dimensions it is better to use type size_t. For example

void foo( size_t, size_t, int[*][*] );

Take into account that at least the second parameter must precede the declaration of the array because it is used to determine the element type of the array (its size shall be known for the function definition).

The parameter names are not required for a function declaration that is not its definition.

Here is shown how the function can be declared and defined using the variable length array notation.

#include <stdio.h>

void foo( size_t, size_t, int[*][*] );

void foo( size_t m, size_t n, int a[m][n] )
{
    for ( size_t i = 0; i < m; i++ )
    {
        for ( size_t j = 0; j < n; j++ ) printf( "%d ", a[i][j] );
        putchar( '\n' );
    }
}

#define M   2
#define N   3

int main(void) 
{
    int a[M][N] =
    {
        { 1, 2, 3 },
        { 4, 5, 6 }
    };

    foo( M, N, a );

    size_t m = 3;
    size_t n = 2;

    int b[m][n];

    for ( size_t i = 0; i < M; i++ )
    {
        for ( size_t j = 0; j < N; j++ ) b[j][i] = a[i][j];
    }

    putchar( '\n' );

    foo( m, n, b );

    return 0;
}

The program output is

1 2 3 
4 5 6 

1 4 
2 5 
3 6 

The third parameter can be also declared like

void foo( size_t m, size_t n, int a[][n] )

or

void foo( size_t m, size_t n, int ( *a )[n] )

because as it was pointed out the array is adjusted to pointer to its element type.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

Neither of the forms in your question is correct one for this; the latter, with [][] is also syntactically illegal.

You're using a C99 and C11 feature called Variable-Length Array, VLA. To declare properly a function that accepts a VLA, you need to provide the dimensions first, then the actual array, with the parameter names for dimensions in the arguments:

void foo(int m, int n, int array[m][n]);

(within declarations but not in definitions it is also possible to use [*] to specify an unspecified dimension, but I don't see much point in it as this makes it explicit which parameter corresponds to which dimension).

Notice that the parameters (m and n) that give the dimensions must occur before the array in the parameter list.

0

When declaring variables, it's important to understand how the memory is laid out.

Linear / Stack-based Arrays

The following declares a 2D array, with 5x5 elements on the 'stack':

void foo(void) {
    int a[5][5];

    ...
}

The memory looks like this:

Liner/Stack Array

If you were to pass this array into a function, then you would be passing the address of the first element a[0][0] (as indicated by the purple arrow). When the receiving function then wishes to access the second dimension, then it needs to know the 'stride', something you do not provide with a prototype of void foo(int a[][]) - hence the error.

Pointer / Heap-based Arrays

The following will declare the same 5x5 2D array on the 'heap' (it is possible to do this all on the stack):

void foo(void) {
    int **a;
    int *a_data;
    int i;

    a_data = malloc(sizeof(*a_data) * (5 * 5);
    a = malloc(sizeof(*a) * 5);

    for (i = 0; i < 5; i++) {
        a[i] = &(a_data[i * 5]);
    }

    ...

    free(a);
    free(a_data);
}

The memory looks like this:

Pointers/Heap Array

When passing this to a function, you pass the address of the 'first dimension'. As the memory structure is different here, we can easily look up the address that the dimension begins at, and the 'stride' is now irrelevant.

Attie
  • 6,690
  • 2
  • 24
  • 34