I tried to assign two fixed-size arrays to an array of pointers to them, but the compiler warns me and I don\'t understand why.
int A[5][5];
int B[5][5];
int
You are being confused by the equivalence of arrays and pointers.
When you declare an array like A[5][5]
, because you have declared both dimensions, C will allocate memory for 25 objects contiguously. That is, memory will be allocated like this:
A00, A01, ... A04, A10, A11, ..., A14, A20, ..., A24, ...
The resulting object, A
, is a pointer to the start of this block of memory. It is of type int *
, not int **
.
If you want a vector of pointers to arrays, you want to declare your variables as:
int *A[5], *B[5];
That would give you:
A0, A1, A2, A3, A4
all of type int*
, which you would have to fill using malloc()
or whatever.
Alternatively, you could declare C
as int **C
.
Either you should declare the third array like
int A[5][5];
int B[5][5];
int ( *C[] )[N][N] = { &A, &B };
that is as an array of pointers to two-dimensional arrays.
For example
#include <stdio.h>
#define N 5
void output( int ( *a )[N][N] )
{
for ( size_t i = 0; i < N; i++ )
{
for ( size_t j = 0; j < N; j++ ) printf( "%2d ", ( *a )[i][j] );
printf( "\n" );
}
}
int main( void )
{
int A[N][N] =
{
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 },
{ 16, 17, 18, 19, 20 },
{ 21, 22, 23, 24, 25 }
};
int B[N][N] =
{
{ 25, 24, 23, 22, 21 },
{ 20, 19, 18, 17, 16 },
{ 15, 14, 13, 12, 11 },
{ 10, 9, 8, 7, 6 },
{ 5, 4, 3, 2, 1 }
};
/*
typedef int ( *T )[N][N];
T C[] = { &A, &B };
*/
int ( *C[] )[N][N] = { &A, &B };
output( C[0] );
printf( "\n" );
output( C[1] );
printf( "\n" );
}
The program output is
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
25 24 23 22 21
20 19 18 17 16
15 14 13 12 11
10 9 8 7 6
5 4 3 2 1
or like
int A[5][5];
int B[5][5];
int ( *C[] )[N] = { A, B };
that is as an array of pointers to the first elements of two-dimensional arrays.
For example
#include <stdio.h>
#define N 5
void output( int ( *a )[N] )
{
for ( size_t i = 0; i < N; i++ )
{
for ( size_t j = 0; j < N; j++ ) printf( "%2d ", a[i][j] );
printf( "\n" );
}
}
int main( void )
{
int A[N][N] =
{
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 },
{ 16, 17, 18, 19, 20 },
{ 21, 22, 23, 24, 25 }
};
int B[N][N] =
{
{ 25, 24, 23, 22, 21 },
{ 20, 19, 18, 17, 16 },
{ 15, 14, 13, 12, 11 },
{ 10, 9, 8, 7, 6 },
{ 5, 4, 3, 2, 1 }
};
/*
typedef int ( *T )[N];
T C[] = { A, B };
*/
int ( *C[] )[N] = { A, B };
output( C[0] );
printf( "\n" );
output( C[1] );
printf( "\n" );
}
The program output is the same as above
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
25 24 23 22 21
20 19 18 17 16
15 14 13 12 11
10 9 8 7 6
5 4 3 2 1
depending on how you are going to use the third array.
Using typedefs (shown in the demonstrative program as commented) ssimplifies the arrays' definitions.
As for this declaration
int*** C = {&A, &B};
then in the left side there is declared a pointer of type int ***
that is a scalar object while in the right side there is a list of initializers that have different type int ( * )[N][N]
.
So the compiler issues a message.
There is a lot wrong with the line
int*** C = {&A, &B};
You're declaring a single pointer C
, but you're telling it to point to multiple objects; that won't work. What you need to do is declare C
as an array of pointers to those arrays.
The types of both &A
and &B
are int (*)[5][5]
, or "pointer to 5-element array of 5-element array of int
"; thus, the type of C needs to be "array of pointer to 5-element array of 5-element array of int
", or
int (*C[2])[5][5] = { &A, &B };
which reads as
C -- C is a
C[2] -- 2-element array of
*C[2] -- pointers to
(*C[2])[5] -- 5-element arrays of
(*C[2])[5][5] -- 5-element arrays of
int (*C[2])[5][5] -- int
Yuck. That's pretty damned ugly. It gets even uglier if you want to access an element of either A
or B
through C
:
int x = (*C[0])[i][j]; // x = A[i][j]
int y = (*C[1])[i][j]; // y = B[i][j]
We have to explicitly dereference C[i]
before we can index into the array it points to, and since the subscript operator []
has higher precedence than the unary *
operator, we need to group *C[0]
in parens.
We can clean this up a little bit. Except when it is the operand of the sizeof
or unary &
operators (or is a string literal being used to initialize another 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 of the array.
The expressions A
and B
have type int [5][5]
, or "5-element array of 5-element array of int
". By the rule above, both expressions "decay" to expressions of type "pointer to 5-element array of int
", or int (*)[5]
. If we initialize the array with A
and B
instead of &A
and &B
, then we need an array of pointers to 5-element arrays of int
, or
int (*C[2])[5] = { A, B };
Okay, that's still pretty eye-stabby, but that's as clean as this is going to get without typedefs.
So how do we access elements of A
and B
through C
?
Remember that the array subscript operation a[i]
is defined as *(a + i)
; that is, given a base address a
, offset i
elements (not bytes)1 from that address and dereference the result. This means that
*a == *(a + 0) == a[0]
Thus,
*C[i] == *(C[i] + 0) == C[i][0]
Putting this all together:
C[0] == A // int [5][5], decays to int (*)[5]
C[1] == B // int [5][5], decays to int (*)[5]
*C[0] == C[0][0] == A[0] // int [5], decays to int *
*C[1] == C[1][0] == B[0] // int [5], decays to int *
C[0][i] == A[i] // int [5], decays to int *
C[1][i] == B[i] // int [5], decays to int *
C[0][i][j] == A[i][j] // int
C[1][i][j] == B[i][j] // int
We can index C
as though it were a 3D array of int
, which is a bit cleaner than (*C[i)[j][k]
.
This table may also be useful:
Expression Type "Decays" to Value
---------- ---- ----------- -----
A int [5][5] int (*)[5] Address of A[0]
&A int (*)[5][5] Address of A
*A int [5] int * Value of A[0] (address of A[0][0])
A[i] int [5] int * Value of A[i] (address of A[i][0])
&A[i] int (*)[5] Address of A[i]
*A[i] int Value of A[i][0]
A[i][j] int Value of A[i][j]
Note that A
, &A
, A[0]
, &A[0]
, and &A[0][0]
all yield the same value (the address of an array and the address of the first element of the array are always the same), but the types are different, as shown in the table above.
p
contains the address of an int
object, then p+1
yields the address of the next int
object, which may be 2 to 4 bytes away.