The C standard says “an array decays to a pointer to its first element.” Many programmers hear this and conclude that arrays and pointers are the same thing. They are not — and that misunderstanding causes real bugs. Here is the precise difference.

What They Share

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};

    /* These three expressions are identical */
    printf("%dn", arr[2]);      /* 30 */
    printf("%dn", *(arr + 2));  /* 30 */
    printf("%dn", 2[arr]);      /* 30 — bizarre but valid */

    return 0;
}

Array subscript notation arr[i] is literally defined as *(arr + i). The compiler converts every array access to pointer arithmetic. In this sense, arrays and pointers behave identically for element access.

Where They Differ: sizeof

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr   = arr;

    printf("sizeof(arr) = %zun", sizeof(arr));   /* 20 bytes (5 ints × 4) */
    printf("sizeof(ptr) = %zun", sizeof(ptr));   /* 8 bytes (pointer size) */

    return 0;
}

sizeof(arr) gives the total size of the array in bytes. sizeof(ptr) gives the size of the pointer itself. This is the most important difference. As we cover in detail in our article on sizeof edge cases, this information is lost the moment an array is passed to a function.

Where They Differ: Assignability

int arr[] = {1, 2, 3};
int *ptr   = arr;

ptr = arr;    /* fine — reassigning a pointer variable */
arr = ptr;    /* ERROR — array names are not assignable */
arr++;        /* ERROR — cannot increment an array */
ptr++;        /* fine — pointer arithmetic */

An array name is a non-modifiable lvalue. It refers to a fixed memory location. A pointer is a regular variable that holds an address — you can change it to point anywhere.

Where They Differ: Storage

int arr[5];   /* allocates 5 ints on the stack — 20 bytes of actual storage */
int *ptr;     /* allocates 8 bytes on the stack — just a pointer variable */
              /* ptr doesn't point to anything valid yet */

Declaring an array allocates the actual storage. Declaring a pointer allocates space for the pointer only — it does not allocate the data it will point to. For the full memory model, see our guide on how pointers work in C.

Array Decay — When They Become Identical

#include <stdio.h>

void print_first(int *p) {
    printf("%dn", *p);
}

int main() {
    int arr[] = {10, 20, 30};
    print_first(arr);    /* arr decays to &arr[0] here */
    print_first(&arr[0]); /* same thing, explicit */
    return 0;
}

When an array is used in most expressions — passed to a function, used in arithmetic, assigned to a pointer — it “decays” to a pointer to its first element. The three exceptions where decay does not happen: sizeof, & (address-of), and string literal initialisation of a char array.

The & Difference

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30};

    printf("arr      = %pn", (void*)arr);       /* address of first element */
    printf("&arr     = %pn", (void*)&arr);       /* address of the array itself */
    printf("arr + 1  = %pn", (void*)(arr + 1));  /* next int (4 bytes forward) */
    printf("&arr + 1 = %pn", (void*)(&arr + 1)); /* next array (12 bytes forward) */

    return 0;
}

arr and &arr produce the same numeric address, but they are different types. arr is int* — adding 1 moves 4 bytes. &arr is int(*)[3] — adding 1 moves the size of the whole array (12 bytes). This matters when working with 2D arrays and function parameters.

2D Arrays vs Pointer to Pointer

#include <stdio.h>

void print_matrix(int rows, int cols, int mat[rows][cols]) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%3d", mat[i][j]);
        }
        printf("n");
    }
}

int main() {
    int m[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    print_matrix(3, 4, m);
    return 0;
}

A 2D array is a contiguous block of memory. A pointer-to-pointer (int**) is an array of pointers — each pointing to a separate allocation. They are not compatible and cannot be used interchangeably as function parameters.

Quick Reference

Array int arr[5] Pointer int *ptr
Allocates storage Yes (5 ints) No (pointer only)
sizeof Total array size Pointer size (8 bytes)
Reassignable No Yes
Incrementable No Yes
In function param Decays to pointer Pointer
Element access arr[i] ptr[i] or *(ptr+i)

TL;DR

  • arr[i] and *(arr + i) are identical — array subscript is pointer arithmetic
  • sizeof(array)sizeof(pointer) — the most important practical difference
  • Arrays cannot be reassigned or incremented; pointers can
  • Arrays decay to pointers when passed to functions — size information is lost
  • &arr has type int(*)[N], not int* — adding 1 jumps the whole array
  • 2D arrays and pointer-to-pointer (int**) are not interchangeable