user14485881
user14485881

Reputation:

How is `int (*arr1)[10]` different from `int arr2[10]`?

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

Answers (2)

Vlad from Moscow
Vlad from Moscow

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

John Bode
John Bode

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

Related Questions