Docs

Pointers and Arrays

Pointers and Arrays in C

Table of Contents

  1. •Introduction
  2. •Array Names as Pointers
  3. •Pointer Arithmetic
  4. •Array Access Methods
  5. •Differences Between Arrays and Pointers
  6. •Pointers with 2D Arrays
  7. •Array of Pointers
  8. •Pointer to Array
  9. •Common Patterns
  10. •Best Practices
  11. •Summary

Introduction

Arrays and pointers are closely related in C. Understanding their relationship is crucial for:

  • •Efficient array manipulation
  • •Passing arrays to functions
  • •Dynamic memory allocation
  • •String handling
  • •Building complex data structures

Array Names as Pointers

The Fundamental Connection

An array name acts as a constant pointer to the first element.

int arr[5] = {10, 20, 30, 40, 50};

arr      →  pointer to arr[0]
&arr[0]  →  same address as arr
*arr     →  value of arr[0] (10)

Visual Representation

int arr[5] = {10, 20, 30, 40, 50};

Memory Layout:
Address:  1000      1004      1008      1012      1016
        ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
        │   10    │   20    │   30    │   40    │   50    │
        ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
          arr[0]    arr[1]    arr[2]    arr[3]    arr[4]
            ↑
           arr (points here, address 1000)

Demonstration

int arr[5] = {10, 20, 30, 40, 50};

printf("%p\n", arr);       // Address of first element
printf("%p\n", &arr[0]);   // Same address!
printf("%d\n", *arr);      // 10 (value at first element)
printf("%d\n", arr[0]);    // 10 (same value)

Pointer Arithmetic

How Pointer Arithmetic Works

When you add/subtract from a pointer, it moves by the size of the type.

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

ptr + 1  →  Address of arr[1] (moves 4 bytes for int)
ptr + 2  →  Address of arr[2] (moves 8 bytes)
ptr + n  →  Address of arr[n]

Mathematical Formula

New Address = Base Address + (n Ɨ sizeof(type))

For int arr[] and int *ptr = arr:
ptr + 1 = 1000 + (1 Ɨ 4) = 1004
ptr + 2 = 1000 + (2 Ɨ 4) = 1008

Operators on Pointers

OperationMeaningExample
ptr + nAddress n elements afterptr + 3 → 4th element
ptr - nAddress n elements beforeptr - 2 → 2 elements back
ptr++Move to next elementAdvance pointer
ptr--Move to previous elementMove back
ptr2 - ptr1Number of elements betweenDistance

Incrementing Pointers

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

printf("%d\n", *ptr);     // 10
ptr++;
printf("%d\n", *ptr);     // 20
ptr++;
printf("%d\n", *ptr);     // 30

Type-Specific Movement

char *cp = (char*)1000;
int *ip = (int*)1000;
double *dp = (double*)1000;

cp + 1  →  1001  (moves 1 byte)
ip + 1  →  1004  (moves 4 bytes)
dp + 1  →  1008  (moves 8 bytes)

Array Access Methods

Four Equivalent Ways to Access Elements

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

// All of these access arr[2] (value 30):
arr[2]       // Array indexing
*(arr + 2)   // Pointer arithmetic on array name
ptr[2]       // Indexing with pointer
*(ptr + 2)   // Pointer arithmetic on pointer

The Equivalence

// The compiler converts array notation to pointer arithmetic:
arr[i]  ≔  *(arr + i)
ptr[i]  ≔  *(ptr + i)

// This even works:
2[arr]  ≔  *(2 + arr)  ≔  *(arr + 2)  // Valid but confusing!

Loop Examples

int arr[5] = {10, 20, 30, 40, 50};

// Method 1: Array indexing
for (int i = 0; i < 5; i++) {
    printf("%d ", arr[i]);
}

// Method 2: Pointer with indexing
int *ptr = arr;
for (int i = 0; i < 5; i++) {
    printf("%d ", ptr[i]);
}

// Method 3: Pointer arithmetic
ptr = arr;
for (int i = 0; i < 5; i++) {
    printf("%d ", *(ptr + i));
}

// Method 4: Incrementing pointer
ptr = arr;
for (int i = 0; i < 5; i++) {
    printf("%d ", *ptr++);
}

// Method 5: Pointer comparison
for (int *p = arr; p < arr + 5; p++) {
    printf("%d ", *p);
}

Differences Between Arrays and Pointers

Despite their similarities, arrays and pointers have important differences.

Key Differences

AspectArrayPointer
Sizesizeof(arr) = total bytessizeof(ptr) = pointer size
AssignmentCannot reassignCan reassign
MemoryAllocates space for elementsOnly stores an address
Address&arr is array address&ptr is pointer's address

sizeof Difference

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

sizeof(arr)  →  20 (5 Ɨ 4 bytes)
sizeof(ptr)  →  8  (pointer size on 64-bit)

// Calculate array length
int length = sizeof(arr) / sizeof(arr[0]);  // 5

Assignment Difference

int arr[5] = {1, 2, 3, 4, 5};
int arr2[5] = {6, 7, 8, 9, 10};
int *ptr = arr;

ptr = arr2;   // OK - pointer can be reassigned
arr = arr2;   // ERROR! Array name is constant

Address Difference

int arr[5];
int *ptr = arr;

arr      →  Address of first element (e.g., 1000)
&arr     →  Address of entire array (1000, but different type!)
&arr[0]  →  Address of first element (1000)

ptr      →  Value stored in ptr (1000)
&ptr     →  Address of ptr itself (different, e.g., 2000)

Visual: &arr vs arr

int arr[5];

arr and &arr have same value but different types:

arr     →  int*       (pointer to int)
&arr    →  int (*)[5] (pointer to array of 5 ints)

arr + 1    moves 4 bytes (to next int)
&arr + 1   moves 20 bytes (past entire array!)

Pointers with 2D Arrays

Memory Layout of 2D Arrays

2D arrays are stored in row-major order (rows are contiguous).

int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

Memory (contiguous):
ā”Œā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”
│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │10 │11 │12 │
ā””ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”˜
 [0][0]....[0][3] [1][0]....[1][3] [2][0]....[2][3]
 └── row 0 ā”€ā”€ā”˜    └── row 1 ā”€ā”€ā”˜    └── row 2 ā”€ā”€ā”˜

Accessing 2D Array with Pointers

int matrix[3][4] = {...};

// matrix is pointer to first row (array of 4 ints)
// matrix[0] is pointer to first element of first row

// These are equivalent:
matrix[i][j]
*(*(matrix + i) + j)
*(matrix[i] + j)
*((int*)matrix + i * 4 + j)  // Flat access

Pointer to First Element

int matrix[3][4];
int *ptr = &matrix[0][0];  // or (int*)matrix

// Access any element:
*(ptr + i * 4 + j)  // matrix[i][j]

Pointer to Row

int matrix[3][4];
int (*rowPtr)[4] = matrix;  // Pointer to array of 4 ints

rowPtr[0]    →  First row
rowPtr[1]    →  Second row
rowPtr[1][2] →  Element at row 1, column 2

Array of Pointers

An array of pointers stores addresses instead of values.

Declaration

int *ptrArray[5];  // Array of 5 pointers to int

Common Use: String Array

char *names[] = {
    "Alice",
    "Bob",
    "Charlie"
};

// Each element is a pointer to a string

Visual Representation

char *names[] = {"Alice", "Bob", "Charlie"};

Array of Pointers:          Strings in Memory:
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”             ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ names[0] ───┼──────────→  │ "Alice\0"     │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤             ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
│ names[1] ───┼──────────→  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤             │ "Bob\0"       │
│ names[2] ───┼──────────→  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜             ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
                            │ "Charlie\0"   │
                            ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Ragged Arrays

Array of pointers can point to arrays of different lengths.

int row0[] = {1, 2};
int row1[] = {3, 4, 5, 6};
int row2[] = {7, 8, 9};

int *rows[] = {row0, row1, row2};

// row0 has 2 elements
// row1 has 4 elements
// row2 has 3 elements

Pointer to Array

Declaration

int (*ptr)[5];  // Pointer to array of 5 ints

Difference from Array of Pointers

int *ptr[5];    // Array of 5 pointers to int
int (*ptr)[5];  // Pointer to array of 5 ints

// The parentheses matter!

Usage with 2D Arrays

int matrix[3][5];
int (*ptr)[5] = matrix;  // Pointer to row (array of 5 ints)

ptr[0]     →  First row
ptr[1]     →  Second row
ptr[1][2]  →  Element at row 1, column 2

Visual Comparison

int *ptr[5];          Array of 5 pointers
ā”Œā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”
│ ptr │ ptr │ ptr │ ptr │ ptr │  5 separate pointers
ā””ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”˜

int (*ptr)[5];        Pointer to array of 5
ā”Œā”€ā”€ā”€ā”€ā”€ā”
│ ptr │ ──→ ā”Œā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”
ā””ā”€ā”€ā”€ā”€ā”€ā”˜     │   │   │   │   │   │  One array of 5 ints
            ā””ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”˜

Common Patterns

Pattern 1: Traversing Array with Pointer

void printArray(int *arr, int size) {
    int *end = arr + size;
    while (arr < end) {
        printf("%d ", *arr);
        arr++;
    }
    printf("\n");
}

Pattern 2: Find Element

int* findElement(int *arr, int size, int target) {
    int *end = arr + size;
    for (int *p = arr; p < end; p++) {
        if (*p == target) return p;
    }
    return NULL;
}

Pattern 3: Reverse Array

void reverseArray(int *arr, int size) {
    int *left = arr;
    int *right = arr + size - 1;

    while (left < right) {
        int temp = *left;
        *left = *right;
        *right = temp;
        left++;
        right--;
    }
}

Pattern 4: Copy Array

void copyArray(int *dest, const int *src, int size) {
    const int *end = src + size;
    while (src < end) {
        *dest++ = *src++;
    }
}

Pattern 5: Two-Pointer Technique

// Remove duplicates from sorted array
int removeDuplicates(int *arr, int size) {
    if (size == 0) return 0;

    int *write = arr + 1;
    int *read = arr + 1;
    int *end = arr + size;

    while (read < end) {
        if (*read != *(read - 1)) {
            *write++ = *read;
        }
        read++;
    }
    return write - arr;
}

Best Practices

1. Prefer Array Notation for Clarity

// Clear
arr[i] = value;

// Less clear (though equivalent)
*(arr + i) = value;

2. Use Pointer Arithmetic for Traversal

// Good for sequential access
for (int *p = arr; p < arr + size; p++) {
    process(*p);
}

3. Don't Forget Size Information

// Always pass size when array decays to pointer
void processArray(int *arr, int size);

4. Use const for Read-Only Access

void printArray(const int *arr, int size);

5. Be Careful with Pointer Comparisons

// Only compare pointers to same array
int arr1[5], arr2[5];
int *p1 = arr1;
int *p2 = arr2;

p1 < p2;  // UNDEFINED! Different arrays

6. Avoid Out-of-Bounds Access

int arr[5];
int *ptr = arr;

ptr + 5;   // OK (one past end, for comparison)
*(ptr + 5); // UNDEFINED! Out of bounds

Summary

Key Relationships

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

// These are equivalent:
arr[i]     ≔  *(arr + i)
ptr[i]     ≔  *(ptr + i)
&arr[i]    ≔  arr + i  ≔  ptr + i

Memory Concepts

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Array elements are contiguous in memory             │
│  Pointer arithmetic moves by sizeof(type) bytes      │
│  Array name is a constant pointer to first element   │
│  Array decays to pointer when passed to functions    │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Quick Reference

ExpressionTypeMeaning
arrint*Address of first element
&arrint(*)[N]Address of entire array
arr + iint*Address of arr[i]
*arrintValue of arr[0]
arr[i]intValue at index i
*(arr + i)intSame as arr[i]

Next Steps

After mastering pointers and arrays:

  1. •Study Pointer to Pointer (double pointers)
  2. •Learn Dynamic Memory Allocation (malloc, free)
  3. •Explore Function Pointers
  4. •Practice String manipulation with pointers

"In C, arrays and pointers are different, but they're used so similarly that they often feel like the same thing."

Pointers And Arrays - C Programming Tutorial | DeepML