Array of Pointers vs Pointer to an Array in C

In C programming, the distinction between an array of pointers and a pointer to an array hinges on operator precedence, memory layout, and pointer arithmetic. An array of pointers is a collection where each element stores a memory address pointing to data located elsewhere, whereas a pointer to an array is a single pointer variable that holds the address of an entire contiguous array block. This article breaks down the operational differences between both constructs, examining their declaration syntax, memory footprint, address calculations, and typical use cases.

Syntax and Operator Precedence

The difference in declaration arises from the precedence of the array subscript operator [] over the dereference operator *.

  • Array of Pointers:

    int *arr[5];

    Because [] has higher precedence than *, arr is evaluated first as an array of 5 elements. The * indicates that each element in this array is a pointer to an integer (int *).

  • Pointer to an Array:

    int (*ptr)[5];

    Parentheses override default operator precedence. The *ptr is evaluated first, meaning ptr is a single pointer. The [5] indicates that it points specifically to an array of 5 integers.

Memory Allocation and Layout

The memory footprint and layout of these two constructs differ significantly:

  • int *arr[5]: Allocates memory for 5 separate pointer variables on the stack. On a 64-bit system, this consumes 5 * 8 = 40 bytes. The data items these pointers point to do not need to be contiguous and can reside anywhere in memory (e.g., separate heap allocations or independent variables).
  • int (*ptr)[5]: Allocates memory for a single pointer variable. On a 64-bit system, this occupies only 8 bytes. It points to a single, contiguous block of memory containing 5 integers (5 * 4 = 20 bytes).

Pointer Arithmetic

The most critical operational difference occurs during pointer arithmetic, as the C compiler scales increments based on the type of data being pointed to.

  • For an array of pointers arr:

    arr + 1;

    The expression arr decays into a pointer to its first element (a pointer to an int *). Adding 1 advances the memory address by the size of a pointer (sizeof(int *), typically 8 bytes).

  • For a pointer to an array ptr:

    ptr + 1;

    Adding 1 advances the address by the total size of the entire array it points to: 5 * sizeof(int) (typically 20 bytes). Instead of moving to the next element, it steps over the entire 5-element array to point to where the next contiguous 5-element array would begin.

Accessing and Dereferencing Data

The mechanics for reading and writing data reflect their differing structures:

  • Array of Pointers:
    • arr[i] accesses the pointer stored at index i.
    • *arr[i] or arr[i][0] dereferences that pointer to read the value it references.
    • *(arr[i] + j) or arr[i][j] accesses the j-th element of the sequence pointed to by the i-th pointer.
  • Pointer to an Array:
    • *ptr dereferences the pointer to yield the underlying array, which immediately decays into a pointer to the first element (&(*ptr)[0]).
    • (*ptr)[i] accesses the i-th element within the array. The parentheses around *ptr are required to dereference the pointer before indexing into the array elements.

Common Use Cases

  • Arrays of Pointers are ideal for handling dynamic, non-contiguous data collections such as "ragged" or "jagged" arrays (where rows have varying lengths), string arrays (char *argv[]), and sorting mechanisms where swapping pointers is more efficient than copying large data blocks.
  • Pointers to Arrays are primarily used when passing multidimensional arrays with fixed secondary dimensions to functions (e.g., void process_matrix(int (*matrix)[10], int rows)), preserving row-length boundary information for compiler index calculations.