C fundamentals · lesson 07

Arrays and pointer arithmetic

<code>arr[i]</code> and <code>*(arr + i)</code> compile to the exact same instruction. Once you understand why, arrays and pointers click into place.

in progress
12 min interactive

Arrays are contiguous

An array is a block of identical-sized elements packed together in memory, one after another, no gaps. int arr[4] is 16 contiguous bytes: element 0 at offset 0, element 1 at offset 4, element 2 at offset 8, element 3 at offset 12.

This is what makes indexing fast — there's no searching. The CPU calculates the address of any element in one multiply-and-add: base + index × sizeof(element).

💡
Mental model: an array name is the address of its first element. Indexing is pointer arithmetic. arr[2] means "go to the address of arr, move forward by 2 element-widths, read what's there."

Array decay

In most expressions, an array name decays to a pointer to its first element. This is automatic. You don't write it — it just happens.

c
int arr[4] = {10, 20, 30, 40}; // These all refer to the same thing: arr // decays to &arr[0] — pointer to first element &arr[0] // explicit address of element 0 // These two are IDENTICAL — the compiler generates the same code: arr[2] // syntactic sugar for *(arr + 2) *(arr + 2) // base address + 2 × sizeof(int) → dereference // This also works (commutative addition): 2[arr] // *(2 + arr) == *(arr + 2) — legal C, just absurd

The pointer arithmetic rule

When you add an integer to a pointer, the compiler scales by the element size automatically. Adding 1 to an int* advances the address by 4 bytes, not 1.

c
int arr[4] = {10, 20, 30, 40}; int *p = arr; // p points to arr[0] at address 0x1000 p + 1 // address 0x1004 (not 0x1001!) p + 2 // address 0x1008 *(p + 3) // value 40, same as arr[3] p++; // p now points to arr[1] — advances by sizeof(int)
⚠️
Arrays don't know their own length. C has no bounds checking. arr[10] on a 4-element array compiles without error and reads whatever bytes happen to be 40 bytes past the start of arr. That's a buffer overread — and a security vulnerability.

Passing arrays to functions

You can't actually pass an array to a function. The array decays to a pointer. The function receives the pointer and knows nothing about the original array's size. You must pass the length separately.

c
// These three declarations mean exactly the same thing: void sum(int *arr, int n); void sum(int arr[], int n); void sum(int arr[10], int n); // the 10 is ignored by the compiler // Inside the function, sizeof(arr) = 8 (pointer size), NOT 40. // Always pass the length as a separate argument.

See it in memory

The visualization below shows an array laid out byte by byte in memory. Switch to the "Array indexing" tab to click each element and see the address arithmetic.

array in memory — contiguous elements
Click the "Array indexing" tab, then click any element to see the address arithmetic.
one-line takeaway

arr[i] is syntax sugar for *(arr + i). Arrays and pointers are the same arithmetic.