CMemory

C Pointer Arithmetic: Navigating Memory Buffers Like a Pro

TT
TopicTrick Team
C Pointer Arithmetic: Navigating Memory Buffers Like a Pro

C Pointer Arithmetic: Navigating Memory Buffers Like a Pro


Table of Contents


The Scaling Secret: Type-Aware Arithmetic

When you perform arithmetic on a pointer, C automatically scales the offset by the size of the pointed-to type:

mermaid

This "scaled arithmetic" is the core mechanism behind array indexing. When you write array[i], the CPU doesn't magically know where element i is — the compiler computes base_address + i * sizeof(element_type).

c

Pointer Increment and Decrement

The ++ and -- operators move a pointer forward or backward by exactly one element:

c

Operator precedence matters:

  • *p++ = *(p++) — reads current value, then advances pointer (post-increment)
  • *++p = *(++p) — advances pointer first, then reads (pre-increment)
  • (*p)++ — increments the value at the address, not the pointer itself

Pointer Addition and Subtraction

You can add or subtract an integer to/from a pointer to jump by multiple elements at once:

c

ptrdiff_t (from <stddef.h>) is the signed integer type for pointer differences — use it instead of int or long to ensure portability.


Array Subscripts Are Pointer Arithmetic

This is one of C's most important equivalences: array[i] is identically the same operation as *(array + i). The subscript notation is purely syntactic sugar:

c

The equivalence a[b] == *(a+b) == *(b+a) == b[a] is not a quirk — it is how the C standard defines array subscripting. This also means you can use subscript notation on any pointer, not just arrays declared with [].


Traversing Buffers: The Pointer Walk Pattern

Professional C code uses a "pointer walk" instead of index loops for maximum clarity and often better compiler optimization:

c

The data + 1000 expression computes a "one-past-the-end" pointer — a pointer that is valid to compute but must never be dereferenced. This pattern comes directly from C++'s begin()/end() convention.


Pointer Comparison and Bounds Checking

Pointers to elements within the same array can be compared with <, >, <=, >=:

c

[!WARNING] Comparing pointers from different arrays or allocations with < / > is undefined behavior. Only the == and != operators are valid for comparing pointers from different objects. This is because different allocations may be at arbitrary virtual addresses.


void* Arithmetic: The Exception

A void* pointer has no type, so the compiler does not know the element size — pointer arithmetic on a bare void* is not allowed in standard C:

c

When you need byte-level pointer arithmetic (as in memory allocator implementations or binary protocol parsing), cast to char* or uint8_t* first.


The restrict Keyword: Aliasing Hints for Optimization

When two pointers could potentially point to overlapping memory regions, the compiler must be conservative about reordering memory accesses. The restrict keyword tells the compiler that no other pointer will access the same memory during the function's lifetime:

c

memcpy from <string.h> uses restrict internally — that is one reason it is so fast. memmove does not use restrict (it handles overlapping regions correctly but is slightly slower).


Real-World Applications: Image Processing and Protocol Parsing

Pointer arithmetic is at the core of high-performance data processing:

Image Processing (Pixel Traversal)

c

Binary Protocol Parsing

c

Common Mistakes and Undefined Behavior

c

Frequently Asked Questions

Can I subtract two pointers from different arrays? You can subtract them without undefined behavior (UB) — the result is an integer — but the numeric value is meaningless. Only pointer subtraction between pointers within the same array (or one-past-the-end) is defined and meaningful.

Why does GCC allow void* arithmetic as an extension? GCC (and Clang in GCC-compatibility mode) treat sizeof(void) as 1, allowing pointer arithmetic on void* as a non-standard extension. This is useful for byte-level operations but is not portable to MSVC or Clang in strict mode. Always cast to char* for portable byte arithmetic.

Is pointer arithmetic faster than index-based access? They compile to identical machine code with -O1 and above. The compiler's optimizer converts both forms to the same multiply-and-add address calculation. The real performance advantage of pointer walks is that they sometimes help the compiler's alias analysis — when using restrict, pointer walks communicate aliasing information more naturally.

What is the maximum valid pointer offset? You can advance a pointer up to n elements past the start of an n-element array (one-past-the-end). Advancing further is undefined behavior. The one-past-the-end pointer is valid to compute and compare against, but never to dereference.


Key Takeaway

Pointer arithmetic is the reason C is the undisputed king of buffer processing. By understanding that ptr + n moves n * sizeof(*ptr) bytes forward, you can write tight loops that the compiler can vectorize with SIMD instructions — processing 8, 16, or 32 elements per CPU cycle.

This is why C-based image codecs, network protocol stacks, and database storage engines all rely heavily on pointer walks rather than higher-level abstractions. It is the most direct expression of "data exists in memory; traverse it efficiently."

Read next: Structs, Unions & Data Alignment: Structural Precision →


Part of the C Mastery Course — 30 modules from C basics to expert-level systems engineering.