Real-world analogy — a row of houses
Imagine a street with five houses, numbered 0 to 4. Each house holds one person's score. An array is exactly that — a numbered row of boxes. You visit house number 2 by writing scores[2]. Simple as that.
📝 How to declare and use an array
Declare: int scores[5]; — this creates 5 integer boxes labelled [0] to [4].
Set values: int scores[] = {10, 20, 30, 40, 50}; — fill all boxes at once.
Read a box: scores[2] gives you the value in box number 2 (which is 30).
Change a box: scores[0] = 99; — box 0 now holds 99.
Count elements: sizeof(scores) / sizeof(scores[0]) — divides total bytes by one element's bytes.
Memory picture — int scores[] = {10, 20, 30, 40, 50}
Real-world analogy — a note with a house address
Instead of the house itself, a pointer is a piece of paper that has a house's address written on it. To find the house, you look at the paper, then go to that address. &scores[2] gives you the address of box 2. *p means "go to the address written in p and read what's there".
📝 Two special symbols: & and *
& means "give me the address of" — &x gives the memory address where variable x lives.
* means "go to that address and read the value" — if p holds an address, *p reads the value stored there.
Declare a pointer: int *p; — p is a pointer that can hold the address of any int.
Point p at a variable: p = &x; — now p holds x's address.
Read through p: *p — same as reading x.
Change through p: *p = 99; — changes x to 99 via the pointer.
Memory picture — int x = 42; int *p = &x;
p = also 0x1000 (p points to x)
*p = the value at that address = 42 (same as x)
🌟 The Big Idea — Array name = Pointer to first box
When you write int arr[] = {10, 20, 30};, the name arr automatically holds the address of box [0]. So arr == &arr[0] is always true.
This means you can use a pointer to walk through an array just like using index numbers. Both ways give exactly the same result:
arr[2] is identical to *(arr + 2) — "go to the address of arr, jump 2 boxes forward, read the value".
arr[0] = *(arr+0) · arr[1] = *(arr+1) · arr[2] = *(arr+2)
Array + Pointer — all four ways to read the same element
*(arr+1) = 20 ← pointer arithmetic
p[1] = 20 ← pointer used as array (where int *p = arr)
*(p+1) = 20 ← pointer arithmetic via p
| Array notation | Pointer notation | Meaning |
|---|---|---|
| arr[0] | *(arr + 0) or *arr | Value at box 0 |
| arr[1] | *(arr + 1) | Value at box 1 |
| arr[2] | *(arr + 2) | Value at box 2 |
| &arr[0] | arr | Address of box 0 (= the array name itself) |
| &arr[1] | arr + 1 | Address of box 1 (4 bytes after box 0) |
| sizeof(arr)/sizeof(arr[0]) | n (element count) | Total elements in the array |
arr[i] into *(arr+i) internally. There is zero performance difference. Use whichever form is clearer — arr[i] for readability, *(p+i) or p++ for pointer-walking patterns.sum = 0 and loop through each box, adding its value. The pointer version uses *p to read each box and p++ to move to the next.
nums[] = {3, 6, 2, 8, 5} — add all boxes
#include <stdio.h> int main() { int nums[] = { 3, 6, 2, 8, 5 }; int n = sizeof(nums) / sizeof(nums[0]); int sum = 0; /* Way 1: using array index */ for (int i = 0; i < n; i++) { sum += nums[i]; /* add each box */ printf(" add nums[%d] = %d → sum so far = %d\n", i, nums[i], sum); } printf("\n Total sum = %d\n\n", sum); /* Way 2: same thing using a pointer */ sum = 0; int *p = nums; /* p points to box [0] */ for (int i = 0; i < n; i++) { sum += *p; /* *p = value in current box */ p++; /* move pointer to next box */ } printf(" Total sum (pointer way) = %d\n", sum); return 0; }
add nums[0] = 3 → sum so far = 3 add nums[1] = 6 → sum so far = 9 add nums[2] = 2 → sum so far = 11 add nums[3] = 8 → sum so far = 19 add nums[4] = 5 → sum so far = 24 Total sum = 24 Total sum (pointer way) = 24
nums[i] and *(p+i) are identical. Use nums[i] when you need the index number for printing. Use *p++ when you just want to walk through and process without caring about the position number.product = 1 (not 0) and multiply (*=) instead of add. A pointer p walks from box to box. After each box, p++ moves it forward by 4 bytes (the size of one int).
nums[] = {2, 3, 4, 5} — multiply all boxes
#include <stdio.h> int main() { int nums[] = { 2, 3, 4, 5 }; int n = sizeof(nums) / sizeof(nums[0]); int product = 1; /* MUST start at 1 for product! */ int *p = nums; /* pointer starts at box [0] */ for (int i = 0; i < n; i++) { printf(" product × *p(%d) = %d × %d = %d\n", i, product, *p, product * *p); product *= *p; /* multiply current box value */ p++; /* pointer moves to next box */ } printf("\n Final product = %d\n", product); printf(" Check: 2×3×4×5 = %d\n", 2*3*4*5); return 0; }
product × *p(0) = 1 × 2 = 2 product × *p(1) = 2 × 3 = 6 product × *p(2) = 6 × 4 = 24 product × *p(3) = 24 × 5 = 120 Final product = 120 Check: 2×3×4×5 = 120
p starts at box [1] and walks to the end.
nums[] = {7, 2, 14, 5, 9} — walk and update max
#include <stdio.h> int main() { int nums[] = { 7, 2, 14, 5, 9 }; int n = sizeof(nums) / sizeof(nums[0]); int max = nums[0]; /* assume first box is biggest */ int *p = nums + 1; /* pointer starts at box [1] */ for (int i = 1; i < n; i++, p++) { printf(" Check *p = %d vs max = %d", *p, max); if (*p > max) { max = *p; /* found something bigger! */ printf(" → NEW MAX = %d", max); } printf("\n"); } printf("\n Largest number = %d\n", max); return 0; }
Check *p = 2 vs max = 7 Check *p = 14 vs max = 7 → NEW MAX = 14 Check *p = 5 vs max = 14 Check *p = 9 vs max = 14 Largest number = 14
*p = *p * 2. This changes the actual contents of the array. When we pass the array to a function, the function receives the address of box [0] and can modify all boxes this way.
Before and after doubling each box
#include <stdio.h> /* Function: receives array address, doubles every element */ void doubleAll(int *p, int n) { for (int i = 0; i < n; i++) { *p = *p * 2; /* write new value into box */ p++; /* move to next box */ } } void printArr(int *arr, int n) { printf(" [ "); for (int i = 0; i < n; i++) printf("%d ", arr[i]); printf("]\n"); } int main() { int nums[] = { 5, 10, 15, 20 }; int n = sizeof(nums) / sizeof(nums[0]); printf("Before: "); printArr(nums, n); doubleAll(nums, n); /* nums decays to &nums[0] */ printf("After: "); printArr(nums, n); return 0; }
Before: [ 5 10 15 20 ] After: [ 10 20 30 40 ]
doubleAll(nums, n), C passes the address of box [0] — not a copy of the array. This means the function can modify the real array. This is why *p = *p * 2 inside the function changes the original nums array.
lo starts at the left (box [0]) and hi starts at the right (box [4]). They move inward — lo++ and hi-- — until they meet. This two-pointer pattern is used in many important algorithms.
Two pointers closing in from both ends
Step 2: *lo=2 + *hi=4 = 6 (lo++ hi--)
Step 3: lo == hi (middle element 3) → stop
#include <stdio.h> int main() { int nums[] = { 1, 2, 3, 4, 5 }; int n = sizeof(nums) / sizeof(nums[0]); int *lo = nums; /* left pointer → box [0] */ int *hi = nums + n - 1; /* right pointer → box [4] */ printf("Pair sums (first+last, moving inward):\n"); while (lo < hi) { /* stop when they meet */ printf(" *lo=%d + *hi=%d = %d\n", *lo, *hi, *lo + *hi); lo++; /* left pointer moves right */ hi--; /* right pointer moves left */ } if (lo == hi) /* odd count → middle element */ printf(" Middle element: *lo = %d (no pair)\n", *lo); printf("\nAll pairs done!\n"); return 0; }
Pair sums (first+last, moving inward): *lo=1 + *hi=5 = 6 *lo=2 + *hi=4 = 6 Middle element: *lo = 3 (no pair) All pairs done!
lo < hi.- Array — a numbered row of boxes. Declare:
int arr[5]. Fill:int arr[] = {10,20,30}. Read box i:arr[i]. Count elements:sizeof(arr)/sizeof(arr[0]). Boxes are numbered from 0. - Pointer — stores an address (location). Declare:
int *p. Get address:p = &x(the & symbol). Read value at address:*p(the * symbol). Change value at address:*p = 99. - Array name = pointer to box [0].
arrand&arr[0]are the same address. Soarr[i]and*(arr+i)are identical. You can assign:int *p = arrthen usep[i]or*(p+i)just likearr[i]. - p++ moves the pointer to the next box (not next byte — next element). After
p++,*preads the next box. This is how you walk an array with a pointer loop. - Sum: start sum=0, add each box. Product: start product=1, multiply each box. Max: start max=arr[0], update when bigger found. Never start product at 0 or max at 0.
- Arrays pass to functions as pointers — the function gets the address of box [0] and can modify the real array. Writing
*p = valueinside a function changes the original array — no copy is made. - Two pointers: lo starts at box [0], hi starts at last box. Move inward with
lo++andhi--untillo < hiis false. Used for pair sums, palindrome check, reverse.