C pointer arithmetic navigate memory arrays tutorial
|

C Pointer Arithmetic: How Pointers Navigate Memory (2026)

C pointer arithmetic is the reason pointers and arrays are so closely connected in C. When you add 1 to a pointer, you do not move one byte forward. You move forward by the size of the data type the pointer points to. This single rule explains how pointers traverse arrays, how string functions work, and how memory allocation libraries operate under the hood.

In this lesson from our C Roadmap, you will master every pointer arithmetic operation with clear memory diagrams and practical code examples. Make sure you understand C Pointers before continuing.

What Is C Pointer Arithmetic?

Pointer arithmetic means performing mathematical operations on pointer values. But unlike regular math, pointer math is scaled by the size of the pointed-to type. The compiler automatically multiplies your offset by sizeof(type).

The allowed operations are:

1. Increment/decrement a pointer (ptr++, ptr--)

2. Add/subtract an integer to/from a pointer (ptr + n, ptr - n)

3. Subtract two pointers of the same type (ptr1 - ptr2)

4. Compare two pointers (ptr1 < ptr2, ptr1 == ptr2)

Everything else (adding two pointers, multiplying pointers, dividing pointers) is illegal.

Pointer Increment and Decrement

When you increment a pointer, it advances by sizeof(*ptr) bytes:

#include <stdio.h>

int main(void) {
    int arr[] = {10, 20, 30, 40, 50};
    int *p = arr;  // Points to arr[0]

    printf("p  = %p, *p = %d\n", (void *)p, *p);   // arr[0] = 10
    p++;
    printf("p  = %p, *p = %d\n", (void *)p, *p);   // arr[1] = 20
    p++;
    printf("p  = %p, *p = %d\n", (void *)p, *p);   // arr[2] = 30

    return 0;
}

Each p++ moves the pointer forward by 4 bytes (the size of int on most systems), not 1 byte. The compiler handles this automatically based on the pointer type.

Here is the memory layout:

Address:  0x1000  0x1004  0x1008  0x100C  0x1010
Value:    [  10  ] [  20  ] [  30  ] [  40  ] [  50  ]
          ^        ^        ^
          p        p+1      p+2

Compare this with a char * pointer, where each increment moves only 1 byte:

char str[] = "Hello";
char *cp = str;

printf("%c\n", *cp);    // 'H'
cp++;
printf("%c\n", *cp);    // 'e' (moved 1 byte)

This is why pointer types matter, as we discussed in C Variables & Data Types. The type determines the step size.

Adding and Subtracting Integers

You can add or subtract any integer value to/from a pointer:

#include <stdio.h>

int main(void) {
    int arr[] = {100, 200, 300, 400, 500};
    int *p = arr;

    printf("*(p + 0) = %d\n", *(p + 0));  // 100
    printf("*(p + 2) = %d\n", *(p + 2));  // 300
    printf("*(p + 4) = %d\n", *(p + 4));  // 500

    // Same as array indexing!
    printf("arr[2] = %d\n", arr[2]);       // 300
    // arr[2] is actually *(arr + 2) internally

    // You can even do this (don't, but it works):
    printf("2[arr] = %d\n", 2[arr]);       // 300!
    // Because a[b] = *(a + b) = *(b + a) = b[a]

    return 0;
}

The expression *(p + n) is exactly equivalent to p[n]. In fact, the compiler converts all array indexing to pointer arithmetic internally. This is the deep connection between C Arrays and pointers in C.

Subtracting Two C Pointers

When you subtract two pointers of the same type, the result is the number of elements between them (not bytes):

#include <stdio.h>
#include <stddef.h>

int main(void) {
    int arr[] = {10, 20, 30, 40, 50};
    int *start = &arr[0];
    int *end = &arr[4];

    ptrdiff_t diff = end - start;
    printf("Elements between: %td\n", diff);  // 4

    // Useful for calculating array length
    int *beyond = arr + 5;  // One past the last element
    printf("Array length: %td\n", beyond - arr);  // 5

    return 0;
}

The result type is ptrdiff_t (from <stddef.h>), a signed integer type. Use %td to print it. Subtracting pointers that point into different arrays is undefined behavior.

C Pointer Comparison

You can compare pointers using relational operators when they point into the same array:

#include <stdio.h>

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    int *p = arr;
    int *q = arr + 3;

    if (p < q) {
        printf("p comes before q in memory\n");
    }

    // Common pattern: iterate with pointer comparison
    int *end = arr + 5;
    for (int *it = arr; it < end; it++) {
        printf("%d ", *it);
    }
    printf("\n");

    // Equality comparison (works for any pointers)
    int x = 10;
    int *a = &x;
    int *b = &x;
    if (a == b) {
        printf("a and b point to the same location\n");
    }

    return 0;
}

Equality comparison (==, !=) works for any two pointers of the same type. Relational comparison (<, >, <=, >=) is only defined for pointers within the same array (or one past the end).

Traversing Arrays with C Pointer Arithmetic

Pointer arithmetic is the idiomatic way to walk through arrays in C. Here is a comparison of three equivalent approaches:

#include <stdio.h>

void print_array_index(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);     // Array indexing
    }
    printf("\n");
}

void print_array_ptr(int *arr, int size) {
    int *end = arr + size;
    for (int *p = arr; p < end; p++) {
        printf("%d ", *p);         // Pointer traversal
    }
    printf("\n");
}

void print_array_offset(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", *(arr + i)); // Pointer + offset
    }
    printf("\n");
}

int main(void) {
    int data[] = {5, 10, 15, 20, 25};
    int n = sizeof(data) / sizeof(data[0]);

    print_array_index(data, n);
    print_array_ptr(data, n);
    print_array_offset(data, n);

    return 0;
}

All three produce the same output. The pointer traversal pattern (for (int *p = arr; p < end; p++)) is extremely common in real-world C code. You will see it in the standard library, Linux kernel, and embedded firmware.

This is how C String Functions like strlen() work internally. They walk through a char array one element at a time until they hit the null terminator.

Pointer Arithmetic vs Array Indexing

Are pointers faster than array indexing? Modern compilers optimize both to identical machine code in most cases. Here is the truth:

// These compile to the SAME machine instructions:
arr[i]        // Compiler converts to *(arr + i)
*(arr + i)    // Already pointer arithmetic
*(p++)        // Increment and dereference

Use whichever style is more readable for your situation. Array indexing is clearer for random access. Pointer traversal is clearer for sequential scanning.

Illegal C Pointer Operations

These operations are not allowed and will cause compiler errors or undefined behavior:

int *p, *q;

// Compiler errors:
// p + q;       // Cannot add two pointers
// p * 2;       // Cannot multiply a pointer
// p / 2;       // Cannot divide a pointer
// p % 2;       // Cannot use modulo on a pointer

// Undefined behavior:
// p - q;       // Only valid if both point into the same array

void Pointer Arithmetic

The C standard does not allow arithmetic on void * pointers because the compiler does not know the element size. GCC allows it as an extension (treating it like char *), but for portable code, always cast first:

#include <stdio.h>
#include <string.h>

// Generic memory dump function
void hex_dump(const void *data, size_t size) {
    const unsigned char *bytes = (const unsigned char *)data;
    for (size_t i = 0; i < size; i++) {
        printf("%02X ", bytes[i]);
    }
    printf("\n");
}

int main(void) {
    int x = 0x12345678;
    hex_dump(&x, sizeof(x));  // 78 56 34 12 (little-endian)

    float f = 3.14f;
    hex_dump(&f, sizeof(f));

    return 0;
}

Practical C Pointer Arithmetic Examples

Example 1: Reverse an array in-place

#include <stdio.h>

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

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

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    int n = 5;

    reverse(arr, n);

    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");  // Output: 5 4 3 2 1

    return 0;
}

Example 2: Custom strlen using pointer arithmetic

#include <stdio.h>

size_t my_strlen(const char *s) {
    const char *start = s;
    while (*s != '\0') {
        s++;
    }
    return (size_t)(s - start);  // Pointer subtraction!
}

int main(void) {
    printf("Length: %zu\n", my_strlen("Hello"));  // 5
    printf("Length: %zu\n", my_strlen(""));        // 0
    return 0;
}

This is essentially how the real strlen() works. The pointer subtraction at the end gives us the element count directly. If you want to understand how C handles C Strings, pointer arithmetic is the foundation.

Common C Pointer Arithmetic Pitfalls

1. Forgetting that arithmetic is scaled:

int arr[5];
int *p = arr;
// p + 1 moves 4 bytes (sizeof(int)), NOT 1 byte
// If you need byte-level movement, cast to char *

2. Going out of bounds:

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr + 10;  // Way past the array — undefined behavior
printf("%d", *p);   // Reading garbage or crashing

3. Modifying string literals:

char *s = "Hello";  // Points to read-only memory
*(s + 1) = 'a';     // CRASH: cannot modify string literal

As we covered in our C Strings lesson, string literals are read-only. Use char s[] = "Hello" if you need to modify the string.

Summary

C pointer arithmetic automatically scales operations by the size of the pointed-to type. Incrementing an int * moves 4 bytes, while incrementing a char * moves 1 byte. You can add/subtract integers, subtract two pointers to get element count, and compare pointers within the same array. Array indexing arr[i] is secretly *(arr + i). In the next lesson, we will explore how to pass pointers to functions and why this is essential for modifying data. Keep following our C Roadmap to master C programming.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *