Pointers and Arrays
Arrays and pointers are like two views of the same person -- from different angles they present different forms, but underneath they point to the same contiguous block of memory.
The Array Name Is the Address of the First Element
In most expressions, the array name automatically converts to a pointer to the first element:
int arr[] = {10, 20, 30, 40, 50};
int *p = arr;
printf("%p\n", (void *)arr);
printf("%p\n", (void *)&arr[0]);
printf("%p\n", (void *)p);
All three addresses are identical. arr and &arr[0] are equivalent -- both are the address of the first element.
There are exceptions: sizeof(arr) returns the size of the entire array, not the pointer size. &arr is a pointer to the entire array with type int (*)[5], and its stride is the entire array.
printf("%zu\n", sizeof(arr));
printf("%zu\n", sizeof(p));
On a 64-bit system, these output 20 (5 x 4 bytes) and 8 (pointer size), respectively.
Pointer Traversal of Arrays
Since a pointer can point to array elements, you can traverse the entire array using pointer arithmetic:
int arr[] = {10, 20, 30, 40, 50};
int *p;
int len = sizeof(arr) / sizeof(arr[0]);
for (p = arr; p < arr + len; p++) {
printf("%d ", *p);
}
printf("\n");
arr + len points to the position just past the last element, serving as the loop termination sentinel. This is a very common C pattern.
You can also use subscript notation:
int i;
for (i = 0; i < len; i++) {
printf("%d ", *(arr + i));
}
Or:
int *p = arr;
int i;
for (i = 0; i < len; i++) {
printf("%d ", p[i]);
}
p[i] and *(p + i) are completely equivalent. The subscript operator is syntactic sugar for pointer arithmetic.
Pointer Arithmetic
Pointers support a limited set of arithmetic operations:
Adding and Subtracting Integers
int arr[] = {10, 20, 30, 40, 50};
int *p = arr + 2;
printf("%d\n", *p);
printf("%d\n", *(p + 1));
printf("%d\n", *(p - 1));
Output: 30, 40, 20. p+1 moves forward one int, p-1 moves backward one int.
Pointer Subtraction
Subtracting two pointers within the same array yields the number of elements between them:
int *q = &arr[4];
printf("%ld\n", (long)(q - p));
Output: 2, because p points to arr[2] and q points to arr[4], a difference of 2 elements.
Pointer Comparison
Pointers within the same array can be compared using <, <=, >, >= to determine relative positions:
int *start = arr;
int *end = arr + len;
while (start < end) {
printf("%d ", *start);
start++;
}
Pointer Stride Summary
| Type | Stride (bytes moved by p+1) |
|---|---|
char * |
1 |
int * |
4 |
double * |
8 |
int (*)[5] |
20 |
Stride = sizeof(pointed-to type)
Equivalence of Pointers and Array Subscripts
The following two access forms are completely equivalent:
| Expression | Equivalent Form |
|---|---|
arr[i] |
*(arr + i) |
&arr[i] |
arr + i |
p[i] |
*(p + i) |
&p[i] |
p + i |
The compiler converts subscript operations to pointer arithmetic. arr[3] and 3[arr] are even both legal in C (because *(arr+3) == *(3+arr)), though nobody writes the latter.
However, pointers and arrays have fundamental differences:
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
arris an array;sizeof(arr)= 20; it cannot be reassignedpis a pointer;sizeof(p)= 8 (64-bit); it can be reassigned to point elsewhere
Example
Reverse an array using pointers:
#include <stdio.h>
void reverse(int *arr, int len) {
int *left = arr;
int *right = arr + len - 1;
while (left < right) {
int temp = *left;
*left = *right;
*right = temp;
left++;
right--;
}
}
int main(void) {
int data[] = {1, 2, 3, 4, 5, 6, 7};
int len = sizeof(data) / sizeof(data[0]);
int i;
reverse(data, len);
for (i = 0; i < len; i++) {
printf("%d ", data[i]);
}
printf("\n");
return 0;
}
7 6 5 4 3 2 1
Two-pointer technique: left moves from the beginning toward the end, right moves from the end toward the beginning, swapping the elements they point to until they meet.
The Nature of Arrays as Function Parameters
When an array is passed to a function, the compiler only passes the address of the first element; the array length information is lost. This is "array decay to pointer":
void func(int arr[]) {
}
The parameter int arr[] is equivalent to int *arr. Inside the function:
sizeof(arr)gives the pointer size, not the array size- You cannot use
sizeofto calculate the element count - The length must be passed as a separate parameter
void print_array(int *arr, int len) {
int i;
for (i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
Four equivalent signatures:
void func(int *arr, int len);
void func(int arr[], int len);
void func(int arr[5], int len);
void func(int arr[100], int len);
The number in the square brackets is ignored by the compiler; it merely indicates "this is a pointer".
Example
Deduplicate a sorted array using pointers:
#include <stdio.h>
int unique(int *arr, int len) {
if (len == 0) return 0;
int *dst = arr;
int *src = arr + 1;
int *end = arr + len;
while (src < end) {
if (*src != *dst) {
dst++;
*dst = *src;
}
src++;
}
return (int)(dst - arr + 1);
}
int main(void) {
int data[] = {1, 1, 2, 2, 2, 3, 4, 4, 5};
int len = sizeof(data) / sizeof(data[0]);
int new_len = unique(data, len);
int i;
for (i = 0; i < new_len; i++) {
printf("%d ", data[i]);
}
printf("\n");
return 0;
}
1 2 3 4 5
dst tracks the write position for deduplicated data; src scans the original array. The function returns the new length. This is an in-place deduplication algorithm that requires no extra array.
❓ FAQ
arr = p; is illegal; the array name is a constant pointer. But p = arr; is legal because a pointer variable can be assigned.arr and &arr?arr is the address of the first element, type int *, stride sizeof(int); &arr is the address of the entire array, type int (*)[5], stride sizeof(int[5]). The numeric value is the same, but the types differ.p[-1] legal?*(p-1). If p points into the middle of an array, p[-1] accesses the previous element safely. But accessing outside the array bounds is undefined behavior.📖 Summary
- An array name decays to a pointer to the first element in expressions, except with sizeof and &
arr[i]and*(arr+i)are completely equivalent; subscripts are syntactic sugar for pointer arithmetic- Pointer arithmetic operates in units of stride, where stride = sizeof(pointed-to type)
- Arrays decay to pointers when passed as function parameters; the length must be passed separately
- The two-pointer technique efficiently solves problems like reversal and deduplication
📝 Exercises
- Write a function that rotates an array left by one position (moving the first element to the end) using pointers, without an extra array.
- Write a function
int *find(int *arr, int len, int target)that returns a pointer to the first element equal to target, or NULL if not found. - Given a sorted array, implement binary search using pointers. Return a pointer to the found element, or NULL if not found.



