Reputation:
As I was going though some code examples, I encountered this way of declaration:
int (*arr1)[10]
I am aware of another way of array declaration in C:
int arr2[10]
Is int (*arr1)[10]
doing the same as int arr2[10]
?
Upvotes: 1
Views: 2973
Reputation: 310960
If in this specification
int (*arr1)[10]
you will remove the term in the parentheses you will get int[10]
. The term in the parentheses denotes a pointer. So you have a pointer to an array of 10 elements of the type int. For example
int a[10];
int ( *arr )[10] = &a;
Dereferencing the pointer you will get the array (the object of the array type pointed to by the pointer) itself.
Here is a demonstrative program.
#include <stdio.h>
int main(void)
{
enum { N = 10 };
int a[N] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int ( *arr )[N] = &a;
for ( size_t i = 0; i < N; i++ )
{
printf( "%d ", ( *arr )[i] );
}
putchar( '\n' );
return 0;
}
The program output is
0 1 2 3 4 5 6 7 8 9
Instead of these declarations you could use declarations based on a typedef like
enum { N = 10 };
typedef int Array[N];
Array a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Array *arr = &a;
Upvotes: 0
Reputation: 123458
The differences are as follows:
int (*arr1)[10]; // arr1 is a pointer to a 10-element array of int
int *arr2[10]; // arr2 is a 10-element array of pointer to int
This is important - despite the [10]
at the end, arr1
is not an array; it only stores a single pointer value, which is the address of some 10-element array of int
.
Pointers to arrays crop up in two main circumstances - when an N-dimensional array expression "decays" to a pointer expression, and when we're allocating memory for an N-dimensional array.
First, the decay rule - except when it is the operand of the sizeof
or the 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, or "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. You've probably seen this before with code like:
int arr[10];
int *p = arr; // arr "decays" to a pointer to its first element
The expression arr
"decays" from type "10-element array of int
" to type "pointer to int
".
The exact same thing happens with 2D arrays:
int arr[5][10];
int (*p)[10] = arr;
The expression arr
"decays" from type "5-element array of 10-element array of int
" to type "pointer to 10-element array of int
".
You can also obtain a pointer to a 1D array directly with the unary &
operator:
int arr[10];
int (*p)[10] = &arr; // note presence of & operator here
but that's not very common.
Another use for pointers to arrays is when we want to dynamically allocate a contiguous 2D (or higher-dimensioned) array:
int (*p)[10] = malloc( sizeof *p * 5 ); // allocates space for a 5x10 array of int
The type of the expression *p
is "10-element array of int
", or int [10]
, so sizeof *p
is equivalent to sizeof (int [10])
, so the malloc
call sets aside enough space for 5 10-element arrays of int
. You would index into this array like any other 2D array: p[i][j] = some_value();
. The rows of this array are contiguous in memory and all the same length, and it only requires a single call to free
to deallocate.
You've probably seen code that dynamically allocates 2D arrays in multiple steps - first you allocate an array of pointers, then for each of those pointers you allocate an array of the target type, like so:
int **p = malloc( sizeof *p * R ); // allocates an R-element array of pointer to int
if ( p )
{
for ( size_t i = 0; i < R; i++ )
{
p[i] = malloc( sizeof *p[i] * C ); // allocates a C-element array of int
}
}
The difference here is that in the second case, the rows are not adjacent in memory - it's not a single, continuous block. Also, different rows may be different lengths (what's sometimes called a "jagged" array). You also have to free
each row separately before freeing p
.
The basic rules of pointer declarations are:
T *p; // p is a pointer to T
T *a[N]; // a is an array of pointer to T
T *f(); // f is a function returning pointer to T
T (*a)[N]; // a is a pointer to an N-element array of T
T (*f)(); // f is a pointer to a function returning T
T const *p; // p is a non-const pointer to const T - you can modify p to point
// to a different object, but you cannot modify the thing p points to
const T *p; // same as above
T * const p; // p is a const pointer to non-const T - you can modify what p
// p points to, but you can't modify p to point to a different object
// (which means p needs to be initialized to a valid pointer value
// when it's declared).
T const * const p; // p is a const pointer to const T - you can't update either
// p or *p
const T * const p; // same as above.
Upvotes: 3