C Pointer Arithmetic

Definition

Pointer arithmetic is the process of performing addition, subtraction, increment, and decrement operations on memory addresses. Unlike integer arithmetic, pointer operations automatically scale by the size of the pointed-to data type, enabling safe and efficient traversal of contiguous memory blocks such as arrays, strings, and dynamically allocated buffers.

Core Mechanics & Scaling

When an integer n is added to or subtracted from a pointer ptr of type T*, the compiler calculates the actual byte offset as:

new_address = original_address + (n * sizeof(T))

This scaling ensures that pointer arithmetic operates in terms of logical elements rather than raw bytes.

Valid vs Invalid Operations

OperationValid?Result TypeNotes
ptr + n / ptr - nT*Moves n elements forward/backward
ptr++ / ptr--T*Post/pre increment/decrement by one element
ptr1 - ptr2ptrdiff_tSigned distance in elements. Both pointers must belong to the same array or one-past-the-end
ptr1 == ptr2intCompares memory addresses
ptr1 < ptr2intValid only within same array/object
ptr + ptrN/AUndefined behavior
ptr * n / ptr / nN/AUndefined behavior
void_ptr + nN/AStandard C forbids arithmetic on void* (GCC/Clang allow as extension)

Code Examples

#include <stdio.h>
#include <stddef.h>
int main(void) {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // Points to arr[0]
// Addition and scaling
printf("%d\n", *(ptr + 2)); // 30 (advances 2 * sizeof(int) bytes)
// Iteration with increment
for (int *p = arr; p < arr + 5; p++) {
printf("%d ", *p);
}
printf("\n");
// Pointer subtraction (yields element count)
int *end = arr + 5;
ptrdiff_t count = end - arr; // 5
// One-past-the-end is valid for comparison but invalid for dereference
// *end; // ❌ Undefined behavior
return 0;
}

Rules & Constraints

  • Array Boundary Rule: Pointers may legally point to any element within an array or to the one-past-the-end position. Dereferencing the one-past-the-end address invokes undefined behavior.
  • Same Object Requirement: Pointer subtraction is only defined when both pointers reference elements of the same array (or one-past-the-end). Comparing pointers from unrelated objects yields unspecified results.
  • Signed Result: Pointer subtraction returns ptrdiff_t, a signed integer type capable of representing negative distances. Overflow in pointer subtraction invokes undefined behavior.
  • Contiguous Memory Only: Pointer arithmetic assumes sequential, homogeneous layout. It fails predictably on non-contiguous structures, linked nodes, or fragmented allocations.
  • No Void Arithmetic: Standard C explicitly disallows arithmetic on void* because sizeof(void) is undefined. Cast to char* or specific type first.

Best Practices

  1. Use ptrdiff_t for differences: Never store pointer subtraction results in int or long. ptrdiff_t guarantees correct signed sizing across platforms.
  2. Prefer one-past-the-end for bounds: Loop conditions like p < arr + len are standard, efficient, and idiomatic C.
  3. Validate before dereference: Ensure pointers remain within allocated bounds before accessing *ptr.
  4. Prefer array indexing when possible: arr[i] is often clearer than *(arr + i) and compiles to identical machine code.
  5. Cast void* explicitly: When working with generic memory buffers, cast to char* or uint8_t* for byte-level arithmetic.
  6. Document ownership and lifetime: Pointer arithmetic amplifies risks of use-after-free or buffer overflows. Track allocation boundaries rigorously.

Common Pitfalls

  • 🔴 Off-by-one errors: Looping p <= arr + len instead of p < arr + len dereferences one-past-the-end → undefined behavior.
  • 🔴 Cross-array subtraction: ptr_a - ptr_b where pointers belong to different arrays invokes undefined behavior even if addresses are close.
  • 🔴 Signed overflow in distance: Large arrays can cause ptrdiff_t overflow during subtraction, especially on 32-bit systems.
  • 🔴 Assuming void* math works standardly: ptr + 1 on void* compiles with GCC but fails on strict C compilers or changes behavior when migrating.
  • 🔴 Pointer decay confusion: Passing &arr (type int (*)[5]) vs arr (decays to int*) changes arithmetic scaling and breaks indexing.
  • 🔴 Ignoring alignment requirements: Arithmetic on misaligned pointers (e.g., casting char* to int* and advancing by odd bytes) causes hardware traps on strict architectures.

Standards & Type Safety

  • C89 through C23: Core pointer arithmetic rules remain stable. C23 strengthens pointer provenance rules and clarifies undefined behavior around out-of-bounds traversal.
  • <stddef.h>: Defines ptrdiff_t and size_t. Mandatory for portable pointer difference calculations.
  • Strict Aliasing: Pointer arithmetic cannot bypass type aliasing rules. Accessing int data through a float* pointer (even with correct byte offset) violates strict aliasing and triggers undefined behavior.
  • Compiler Diagnostics: Enable -Warray-bounds, -Wpointer-arith, and -Wall to catch scaling mistakes, out-of-bounds arithmetic, and invalid void pointer usage at compile time.
  • Static Analysis: clang-tidy, cppcheck, and Coverity detect unsafe pointer arithmetic patterns, potential overflows, and provenance violations before runtime.

C Preprocessor, Macros & Compilation Directives (Complete Guide)

https://macronepal.com/aws/mastering-c-variadic-macros-for-flexible-debugging/
Explains variadic macros in C, allowing functions/macros to accept a variable number of arguments for flexible logging and debugging.

https://macronepal.com/aws/mastering-the-stdc-macro-in-c/
Explains the __STDC__ macro, which indicates compliance with the C standard and helps ensure portability across compilers.

https://macronepal.com/aws/c-time-macro-mechanics-and-usage/
Explains the __TIME__ macro, which provides the compilation time of a program and is often used for logging and debugging.

https://macronepal.com/aws/understanding-the-c-date-macro/
Explains the __DATE__ macro, which inserts the compilation date into programs for tracking builds.

https://macronepal.com/aws/c-file-type/
Explains the __FILE__ macro, which represents the current file name during compilation and is useful for debugging.

https://macronepal.com/aws/mastering-c-line-macro-for-debugging-and-diagnostics/
Explains the __LINE__ macro, which provides the current line number in source code, helping in error tracing and diagnostics.

https://macronepal.com/aws/mastering-predefined-macros-in-c/
Explains all predefined macros in C, including their usage in debugging, portability, and compile-time information.

https://macronepal.com/aws/c-error-directive-mechanics-and-usage/
Explains the #error directive in C, used to generate compile-time errors intentionally for validation and debugging.

https://macronepal.com/aws/understanding-the-c-pragma-directive/
Explains the #pragma directive, which provides compiler-specific instructions for optimization and behavior control.

https://macronepal.com/aws/c-include-directive/
Explains the #include directive in C, used to include header files and enable code reuse and modular programming.

HTML Online Compiler
https://macronepal.com/free-html-online-code-compiler/

Python Online Compiler
https://macronepal.com/free-online-python-code-compiler/

Java Online Compiler
https://macronepal.com/free-online-java-code-compiler/

C Online Compiler
https://macronepal.com/free-online-c-code-compiler/

C Online Compiler (Version 2)
https://macronepal.com/free-online-c-code-compiler-2/

Node.js Online Compiler
https://macronepal.com/free-online-node-js-code-compiler/

JavaScript Online Compiler
https://macronepal.com/free-online-javascript-code-compiler/

Groovy Online Compiler
https://macronepal.com/free-online-groovy-code-compiler/

J Shell Online Compiler
https://macronepal.com/free-online-j-shell-code-compiler/

Haskell Online Compiler
https://macronepal.com/free-online-haskell-code-compiler/

Tcl Online Compiler
https://macronepal.com/free-online-tcl-code-compiler/

Lua Online Compiler
https://macronepal.com/free-online-lua-code-compiler/

Leave a Reply

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


Macro Nepal Helper