Eric
Eric

Reputation: 65

Passing multi-dimensional arrays into functions errors

I'm trying to pass a 2-D array into a function from the main function but I keep getting errors. What am I doing wrong? Here's the code:

void modifyArray(int *array);
int main(int argc, char *argv[])
{
int array[3][3];
modifyArray(&array, 3, 3, 3);
}

void modifyArray(int *array, int value, int row, int column)
{
array[row][column] = value;
}

Errors:

 error #2140: Type error in argument 1 to 'modifyArray'; expected 'int *' but found 'int (*)[3][3]'.

Thanks in advance :)

Upvotes: 1

Views: 61

Answers (4)

John Bode
John Bode

Reputation: 123468

First, the boring background:

Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T" and the value of the expression will be the address of the first element.

The type of array is "3-element array of 3-element array of int". Unless it is the operand of the sizeof or unary & operators, it "decays" to an expression of type "pointer to 3-element array of int", or int (*)[3]. The expression &array has type "pointer to 3-element array of 3-element array of int", or int (*)[3][3].

So, you have several possible avenues to deal with this. You can keep the function call the same and make the following changes to the function definition:

modifyArray( &array, 3, 2, 2 ); // A 3-element array is only indexed from 0 to 2
...                             // so writing to array[3][3] writes outside the
                                // bounds off the array, leading to undefined behavior

void modifyArray(int (*array)[3][3], int value, int row, int column)
{
  (*array)[row][column] = value;
}

Alternately, you can change the function call to leave off the & operator:

modifyArray( array, 3, 2, 2 );

and change the function definition to

void modifyArray(int (*array)[3], int value, int row, int column)
{
  array[row][column] = value;
}

Remember that the subscript operation a[i] is equivalent to *(a + i) - given an address a, offset i elements (not bytes) from that address and dereference the result. array is the address of the beginning of a 3-element array, so array + i yields the address of the i'th 3-element array following that address. Thus, *(array + row) is the same as array[row].

Final option - pass a pointer to the first element and treat it as 1D array in the function:

modifyArray( &array[0][0], 3, 2, 2 );
...
void modifyArray(int *array, int value, int row, int column)
{
  array[row * 3 + column] = value;
}

The first two options are limited in that they can only deal with arrays of a specific size - the first can only work with 3x3 arrays and the second can only work with Nx3 arrays. The last option can deal with 2D arrays with any number of rows and columns, you just have to find some way to pass the number of rows.

Upvotes: 0

Vlad from Moscow
Vlad from Moscow

Reputation: 310990

If you had a one-dimensional array then this function declaration

void modifyArray(int *array);

is correct because it is equivalent to the following declaration

void modifyArray(int array[]);

That is the parameter having an array type is adjusted by the compiler to pointer to the array element type.

Though in general it is much better also to pass the number of elements in the array like

void modifyArray(int *array, size_t n);

And if you want to supply an initializer then the function declaration can look like

void modifyArray(int *array, size_t n, int value );

Pay attention to that the two function declarations in your program are different

void modifyArray(int *array);

and

void modifyArray(int *array, int value, int row, int column)

Moreover within the function this statement

array[row][column] = value;

will invoke undefined behavior because there is an access to memory beyond the array.

So if you have an array like this

int array[3][3];

then the corresponding function parameter will look like

void modifyArray( int array[][3], size_t n, int value );

or equivalently like

void modifyArray( int ( *array )[3], size_t n, int value );

So it is better to use a named constant for the integer literal 3 either like

#define  N  3

or like

enum { N = 3 };

And the function declaration will look like

void modifyArray( int ( *array )[N], size_t n, int value );

and the array declaration in main will look like

int array[N][N];

and the function itself can be called like

modifyArray( array, N, 3 );

If you want to set all elements of the array to the initializer then within the function you should write

for ( size_t i = 0; i < n; i++ )
{
    for ( size_t j = 0; j < N; j++ )
    {
        array[i][j] = value;
    }
}

If your compiler supports variable length arrays then the function declaration can look like

void modifyArray( size_t rows, size_t cols, int array[][cols], int value );

and the function can be called like

modifyArray( 3, 3, array, 3 );

In this case the function can be called for any two-dimensional array with arbitrary values of rows and columns.

Within the function the loops can look like

for ( size_t i = 0; i < rows; i++ )
{
    for ( size_t j = 0; j < cols; j++ )
    {
        array[i][j] = value;
    }
}

Upvotes: 0

Eric Postpischil
Eric Postpischil

Reputation: 222826

int array[3][3]; declares array to be an array of three arrays of three int.

&array takes its address, so the result is a pointer to an array of three arrays of three int.

In the declaration of modifyArray, int *array declares the parameter array to be a pointer to an int.

A pointer to an int is a different thing from a pointer to an array of three arrays of three int. So the compiler complains.

In general, if something is declared as a foo, and you pass it to a function, you can declare it in the function declaration as a foo:

void modifyArray(int array[3][3], int value, int row, int column);
int main(int argc, char *argv[])
{
    int array[3][3];
    modifyArray(array, 5, 1, 2);
}

C does engage in some shenanigans with array expressions and parameters. When a parameter is declared as an array, the compiler will adjust it to be a pointer. So declaring int array[3][3] for a parameter actually declares a pointer to an array of three int, int (*array)[3]. Only the outer/base parameter type of array is adjusted; further arrays inside the type are not adjusted.

Similarly, when an array is used in an expression other than as the operand of sizeof or of unary &, or, for string literals, to initialize an array, the compiler converts it to a pointer to its first element. So the call modifyArray(array, 5, 1, 2) actually passes &array[0] to the function. That matches the adjusted type, int (*array)[3], so it works. The resulting pointer can be used with the usual array notation, such as array[row][column].

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409186

Please read the error message properly, it explains what's wrong.

Furthermore, you should be getting more errors than the one you mention, as inside the modifyArray function you use array as an array of arrays which that variable isn't. Not to mention that you have two different declarations of modifyArray.

What every decent beginners book, tutorial or class should have told you is that arrays can decay to pointers to their first element. That is, in the main function when you use plain array then it's the same as &array[0]. And since each element of array is in turn an array, then &array[0] will be a pointer to such an array.

So the correct way to pass your array is simply to pass it as array, and the type needed to handle it will then be int (*)[3]:

void modifyArray(int (*array)[3], int value, size_t row, size_t column)
{
    array[row][column] = value;
}

int main(void)
{
    int array[3][3];

    modifyArray(array, 3, 1, 1);  // Will be the same as array[1][1] = 3
}

On another note, your current code also passes the indexes 3 for both row and column. For an array of 3 elements, the valid indexes are 0 to 2 (inclusive), which means an index of 3 will be out of bounds.

Upvotes: 1

Related Questions