Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing arrays and matrices to functions as pointers and pointers to pointers in C

Given the following code:

void
foo( int* array ) 
{
    // ...
}

void
bar( int** matrix ) 
{
    // ...
}

int
main( void ) {
    int array[ 10 ];
    int matrix[ 10 ][ 10 ];

    foo( array );
    bar( matrix );

    return 0;
}

I don't understand why I get this warning:

warning: passing argument 1 of ‘bar’ from incompatible pointer type

Although 'foo' call seems to be ok.

Thanks :)

like image 896
Auron Avatar asked Feb 13 '09 17:02

Auron


People also ask

Can you pass an array as a pointer in C?

Array can be passed to function in C using pointers and because they are passed by reference changes made on an array will also be reflected on the original array outside function scope.

What is passing array to function in C?

Arrays are declared and initialized before using them in a program. In C/C++ an array when passed as a function argument is always treated as a pointer by a function. Ways to pass an array to a function in C/C++ are Formal parameters as pointers, Formal parameters as sized arrays, Formal parameters as unsized arrays.

How array will be passed to function using pointers with example?

How Arrays are Passed to Functions in C/C++? A whole array cannot be passed as an argument to a function in C++. You can, however, pass a pointer to an array without an index by specifying the array's name. In C, when we pass an array to a function say fun(), it is always treated as a pointer by fun().


4 Answers

Well, it's certainly not well understood by the C community as can be seen by glancing over SO. The magic is, all of the following are totally, 100%, equivalent:

void foo(int (*array)[10]);
void foo(int array[][10]);
void foo(int array[10][10]);
void foo(int array[42][10]);

It is very important to draw the distinction of a pointer and an array. An array is not a pointer. An array can be converted to a pointer to its first element. If you have a pointer you have this:

--------
| ptr  |  -------> data
--------

However, if you have an array, you have this:

---------------------------
| c1 | c2 | c3 | ... | cn |
---------------------------

With the pointer, the data is at a whole other planet, but linked to by the pointer. An array has the data itself. Now, a multi-dimensional array is just an array of arrays. The arrays are nested into a parent array. So, the sizeof of your array is:

(sizeof(int) * 10) * 10

That is because you have 10 arrays, all of which are arrays of 10 integers. Now, if you want to pass that array, it is converted. But to what? A pointer to its first element. The element type is not a pointer, but an array. As a consequence, you pass a pointer to an array of 10 int:

int (*)[10] // a pointer to an int[10]

It is neither a array of int*, nor a int**. You may ask why the array is not passed as an int**. It's because the compiler has to know the row-length. If you do an array[1][0], the compiler will address a place sizeof(int) * 10 bytes apart from the begin of the 2 dimensional array. It decodes that information in the pointer-to-array type.

So, you have to chose among one of the above fully equivalent function prototypes. Naturally, the last one is just confusing. The compiler just silently ignores any number written in the most outer dimension if a parameter is declared to be an array. So i would also not use the second last version. Best is to use the first or second version. What is important to remember is that C has no (real) array parameters! The parameter will be a pointer in the end (pointer to array in this case).

Note how the multi-dimensional case of above is similar to the degenerate, one dimensional case below. All of the following 4 versions are fully equivalent:

void foo(int *array);
void foo(int array[]);
void foo(int array[10]);
void foo(int array[42]);
like image 194
Johannes Schaub - litb Avatar answered Sep 28 '22 09:09

Johannes Schaub - litb


Passing multi-dimensional arrays in C is a tricky subject. See this FAQ.

The question to ask is how you'll be using bar. If you always know it will be passed a 10x10 array then rewrite it as

bar(int matrix[10][10]);

If you want to cope with arrays of varying dimensions then you might have to pass in the lengths:

bar(int *matrix, int width, int height);
like image 40
Mark Pim Avatar answered Sep 28 '22 09:09

Mark Pim


The problem is that the data structure matrix[10][10] is actually not a table of ten pointers to array[10]'s, but it is an sequential array of 100 integers. The proper signature for bar is

bar (int matrix[10][10])

If you actually want to represent the matrix using indirection and have int **matrix as the parameter type for bar, then you need to allocate it differently:

int *matrix[10];
int my_data[100];
int i;
for (i = 0; i < 10; i++) { matrix[i] = &(my_data[i * 10]); }
bar(matrix);

Now 'matrix' matches the type int **. 'matrix' is an array of ten pointers, and you can pass it by pointer, hence getting the second *.

like image 21
Antti Huima Avatar answered Sep 28 '22 07:09

Antti Huima


Here is some code to practice on - it contains all possible types of passing 2dimensional array and code to access element values

#include <stdio.h>

#define NUMROWS 2
#define NUMCOLUMNS 5

#define FILL_ARRAY() \
    *array[0] = '1'; \
    (*array)[7] = '2'; \
    *(array[1]) = '3'; \
    *(*(array+1)+1) = '4'; \
    *(array[0]+3) = '5'; \
    *(*array+2) = '7'; \
    array[0][1] = '6'; 

void multi_01( char (*array)[NUMCOLUMNS] )       { FILL_ARRAY(); }
void multi_02( char array[][NUMCOLUMNS] )        { FILL_ARRAY(); }
void multi_03( char array[NUMROWS][NUMCOLUMNS] ) { FILL_ARRAY(); }
void multi_04( char **array )                    { FILL_ARRAY(); }
void multi_05( char *array[] )                   { FILL_ARRAY(); }
void multi_06( char *array[NUMCOLUMNS] )         { FILL_ARRAY(); }

int main(int argc, char **argv)
{
    int i;
    char mystr[NUMROWS][NUMCOLUMNS] = { { 'X', 'X', 'X', 'X'}, {'X','X','X'} };
    char *pmystr[sizeof(mystr)/sizeof(*mystr)];
    int numcolumns = sizeof(*mystr);
    int numrows = sizeof(mystr)/sizeof(*mystr);
    for( i=0; i<numrows; i++ ) pmystr[i] = *(mystr+i);

    multi_01( mystr );  multi_02( mystr );  multi_03( mystr );
    multi_04( pmystr ); multi_05( pmystr ); multi_06( pmystr );

    printf("array '%s', '%s'\n", mystr[0], mystr[1]);

    getc(stdin);
    return 0;
}
like image 21
Ulterior Avatar answered Sep 28 '22 07:09

Ulterior