Pointers & Arrays — Simple Maths Examples
0%
C Basics  ·  Pointers & Arrays

Pointers & Arrays —
Made Very Simple

No jargon. No complexity. Just boxes in memory, addresses, and five tiny maths programs that make both topics click forever.

📦
Array
A row of boxes that hold numbers, each with an index number [0], [1], [2]…
📍
Pointer
A variable that holds the address (location) of one of those boxes
📦
Part 1 — Arrays
A numbered row of boxes, all the same type, stored one after another in memory
🏠

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}

10
[0]
20
[1]
30
[2]
40
[3]
50
[4]
scores[0]=10   scores[1]=20   scores[2]=30   scores[3]=40   scores[4]=50
📍
Part 2 — Pointers
A variable that stores the memory address (location) of another variable
🗺️

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;

Variable p (pointer)
p = 0x1000
→ points to →
Variable x at address 0x1000
x = 42
&x  = the address = 0x1000
p   = also 0x1000 (p points to x)
*p  = the value at that address = 42 (same as x)
🔗
Part 3 — How Arrays and Pointers are Related
An array name IS a pointer to its first element — this is the most important fact in C

🌟 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

10
[0]
20
[1]
30
[2]
arr[1]  = 20   ← classic index
*(arr+1) = 20   ← pointer arithmetic
p[1]   = 20   ← pointer used as array (where int *p = arr)
*(p+1)  = 20   ← pointer arithmetic via p
Array notationPointer notationMeaning
arr[0]*(arr + 0) or *arrValue at box 0
arr[1]*(arr + 1)Value at box 1
arr[2]*(arr + 2)Value at box 2
&arr[0]arrAddress of box 0 (= the array name itself)
&arr[1]arr + 1Address of box 1 (4 bytes after box 0)
sizeof(arr)/sizeof(arr[0])n (element count)Total elements in the array
arr[i] and *(arr+i) compile to identical machine code. The C compiler converts every 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.
simple maths examples
1
➕ Add All Numbers in an Array
The simplest array loop — add every box's value together to get the total
Sum · Array
We have 5 numbers: 3, 6, 2, 8, 5. We want their sum: 3+6+2+8+5 = 24. The array stores all five numbers. We start with 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

3
[0]
6
[1]
2
[2]
8
[3]
5
[4]
3 + 6 + 2 + 8 + 5 = 24
ex1_sum.c
C
#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;
}
output
  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
Both ways give the same answer. 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.
2
✖️ Multiply All Numbers (Product)
Start with product = 1, multiply each box — pointer increments forward one step at a time
Product · Pointer walk
We have 2 × 3 × 4 × 5 = 120. The only change from the sum example is: start with 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

2
[0]
3
[1]
4
[2]
5
[3]
1 × 2 × 3 × 4 × 5 = 120   (start with 1 not 0!)
ex2_product.c
C
#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;
}
output
  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
Start product at 1, never 0. If you start at 0 and multiply, everything stays 0 (0 × anything = 0). The "identity" for multiplication is 1 (1 × anything = that thing). This is the most common product-loop mistake.
3
🏆 Find the Largest Number
Pointer walks each box — keep updating max whenever a bigger value is found
Maximum · Pointer compare
We have 7, 2, 14, 5, 9 and want the biggest: 14. Start by assuming box [0] is the biggest. Then walk every other box — if the current box is bigger than our current best, update the best. A pointer p starts at box [1] and walks to the end.

nums[] = {7, 2, 14, 5, 9} — walk and update max

7
[0]
2
[1]
14
[2]
5
[3]
9
[4]
max starts = 7 → still 7 (2<7) → updates to 14 → still 14 → still 14. Answer = 14
ex3_max.c
C
#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;
}
output
  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
Start max at the first element, not zero. If all numbers are negative (like -3, -8, -1), starting max at 0 would incorrectly report 0 as the largest. Always start max = nums[0] — the actual first value in the array.
4
×2 Modify Array In-Place — Double Every Number
Pointer writes back to the array — *p = *p × 2 changes the actual box contents
Modify in-place · Write via pointer
We have 5, 10, 15, 20 and want to double every number to get 10, 20, 30, 40. A pointer walks each box and writes a new value back: *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

BEFORE:
5
[0]
10
[1]
15
[2]
20
[3]
⬇️ *p = *p × 2
AFTER:
10
[0]
20
[1]
30
[2]
40
[3]
ex4_double.c
C
#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;
}
output
Before: [ 5 10 15 20 ]
After:  [ 10 20 30 40 ]
Arrays always pass as pointers to functions. When you write 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.
5
↔️ Sum of First + Last, Second + Second-last…
Two pointers — one from the left, one from the right — move inward and add pairs together
Two pointers · Pair sums
We have 1, 2, 3, 4, 5. We want the sums of first+last pairs: 1+5=6, 2+4=6, 3 is the middle. We use two pointers: 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

1
lo→
2
[1]
3
[2]
4
[3]
5
←hi
Step 1: *lo=1 + *hi=5 = 6   (lo++ hi--)
Step 2: *lo=2 + *hi=4 = 6   (lo++ hi--)
Step 3: lo == hi (middle element 3) → stop
ex5_pair_sums.c
C
#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;
}
output
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!
The two-pointer pattern is extremely powerful. It is used to check if a word is a palindrome, find two numbers that add up to a target, and reverse an array in place. The key idea: one pointer starts at the beginning, one at the end, and they move toward each other until they meet. Only possible because arrays are contiguous in memory — the pointers can compare positions with lo < hi.
summary — what you learned
  • 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]. arr and &arr[0] are the same address. So arr[i] and *(arr+i) are identical. You can assign: int *p = arr then use p[i] or *(p+i) just like arr[i].
  • p++ moves the pointer to the next box (not next byte — next element). After p++, *p reads 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 = value inside 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++ and hi-- until lo < hi is false. Used for pair sums, palindrome check, reverse.