Reputation: 65
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
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
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
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
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