CoreTechED

Loading

Pointer Arithmetic in C: Complete Guide with Examples

Learn C pointer arithmetic: increment, decrement, addition, subtraction, pointer difference, and comparison. Master efficient array traversal.

Pointer Arithmetic in C: Complete Guide with Examples

Pointer Arithmetic in C: A Comprehensive Tutorial

Pointer arithmetic refers to the valid arithmetic operations that can be performed on pointers in C. Since pointers store memory addresses, only a limited set of operations is allowed. Understanding pointer arithmetic is essential for efficient array processing, dynamic memory management, and low-level programming.

This tutorial provides an in-depth exploration of pointer arithmetic in C: increment/decrement, addition/subtraction of integers, pointer subtraction, comparison operations, practical applications, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is Pointer Arithmetic?

Pointer arithmetic is the set of operations that can be performed on pointer variables. Unlike regular arithmetic, pointer arithmetic operates based on the size of the data type the pointer points to, not on bytes directly.

Key formula:

text

New Address = Current Address ± (Number × Size of Data Type)

Example:

c

int *ptr; // Points to int (4 bytes) ptr + 1; // Advances by 4 bytes (sizeof(int)) ptr + 2; // Advances by 8 bytes (2 × sizeof(int))

Why Pointer Arithmetic Matters

  • Array traversal: Efficiently move through array elements.
  • Dynamic memory: Navigate allocated memory blocks.
  • Performance: Avoid array indexing overhead.
  • Low-level programming: Direct memory manipulation.
  • Algorithm implementation: Search, sort, and data processing.

Valid Pointer Operations

OperationDescription
ptr++, ptr--Increment/decrement by one element
ptr + n, ptr - nAdd/subtract integer
ptr1 - ptr2Difference between two pointers (same type)
ptr1 == ptr2, ptr1 < ptr2, etc.Comparison
ptr == NULL, ptr != NULLNull check

Invalid Pointer Operations

OperationReason
ptr1 + ptr2Meaningless (adding addresses)
ptr1 * ptr2Not defined
ptr1 / ptr2Not defined
ptr + floatOnly integer arithmetic allowed

Syntax & Core Mechanics

Increment and Decrement

Incrementing moves the pointer to the next element; decrementing moves to the previous.

c

#include <stdio.h> int main(void) { int a = 22; int *p = &a; printf("p = %p\n", (void*)p); p++; printf("p++ = %p (advanced by %zu bytes)\n", (void*)p, sizeof(int)); p--; printf("p-- = %p (restored)\n", (void*)p); return 0; }

Expected output (addresses will vary):

text

p = 0x7ffc1234 p++ = 0x7ffc1238 (advanced by 4 bytes) p-- = 0x7ffc1234 (restored)

Different data types advance by different amounts:

c

#include <stdio.h> int main(void) { int a = 10; float b = 3.14f; char c = 'A'; int *intPtr = &a; float *floatPtr = &b; char *charPtr = &c; printf("int pointer: %p -> ", (void*)intPtr); intPtr++; printf("%p (+%zu bytes)\n", (void*)intPtr, sizeof(int)); printf("float pointer: %p -> ", (void*)floatPtr); floatPtr++; printf("%p (+%zu bytes)\n", (void*)floatPtr, sizeof(float)); printf("char pointer: %p -> ", (void*)charPtr); charPtr++; printf("%p (+%zu byte)\n", (void*)charPtr, sizeof(char)); return 0; }

Expected output (typical):

text

int pointer: 0x7ffc1000 -> 0x7ffc1004 (+4 bytes) float pointer: 0x7ffc0ffc -> 0x7ffc1000 (+4 bytes) char pointer: 0x7ffc0ffb -> 0x7ffc0ffc (+1 byte)

Addition of Integer to Pointer

c

#include <stdio.h> int main(void) { int arr[5] = {10, 20, 30, 40, 50}; int *ptr = arr; printf("ptr = %p, *ptr = %d\n", (void*)ptr, *ptr); ptr = ptr + 2; /* Move forward 2 elements */ printf("ptr + 2 = %p, *ptr = %d\n", (void*)ptr, *ptr); ptr += 2; /* Move forward 2 more elements */ printf("ptr += 2 = %p, *ptr = %d\n", (void*)ptr, *ptr); return 0; }

Expected output:

text

ptr = 0x7ffc1000, *ptr = 10 ptr + 2 = 0x7ffc1008, *ptr = 30 ptr += 2 = 0x7ffc1010, *ptr = 50

Subtraction of Integer from Pointer

c

#include <stdio.h> int main(void) { int arr[5] = {10, 20, 30, 40, 50}; int *ptr = &arr[4]; /* Point to last element */ printf("ptr = %p, *ptr = %d\n", (void*)ptr, *ptr); ptr = ptr - 2; /* Move back 2 elements */ printf("ptr - 2 = %p, *ptr = %d\n", (void*)ptr, *ptr); return 0; }

Expected output:

text

ptr = 0x7ffc1010, *ptr = 50 ptr - 2 = 0x7ffc1008, *ptr = 30

Subtraction of Two Pointers

The result is the number of elements between the two pointers (not bytes).

c

#include <stdio.h> int main(void) { int arr[5] = {10, 20, 30, 40, 50}; int *ptr1 = &arr[1]; int *ptr2 = &arr[4]; ptrdiff_t difference = ptr2 - ptr1; printf("ptr2 - ptr1 = %td elements\n", difference); /* Also works with array name */ ptrdiff_t diff2 = &arr[4] - arr; printf("&arr[4] - arr = %td elements\n", diff2); return 0; }

Expected output:

text

ptr2 - ptr1 = 3 elements &arr[4] - arr = 4 elements

Important: Subtracting pointers from different arrays is undefined behavior.

Comparison of Pointers

c

#include <stdio.h> int main(void) { int arr[5] = {10, 20, 30, 40, 50}; int *ptr1 = &arr[0]; int *ptr2 = &arr[3]; if (ptr1 < ptr2) { printf("ptr1 points to earlier element\n"); } if (ptr1 != ptr2) { printf("ptr1 and ptr2 are different\n"); } /* Comparing array name with first element */ if (arr == &arr[0]) { printf("arr equals &arr[0]\n"); } return 0; }

Expected output:

text

ptr1 points to earlier element ptr1 and ptr2 are different arr equals &arr[0]

Comparison with NULL

c

#include <stdio.h> #include <stddef.h> int main(void) { int *ptr = NULL; if (ptr == NULL) { printf("The pointer is NULL\n"); } return 0; }

Expected output:

text

The pointer is NULL

Full Practical Code Example with Explanation

This program demonstrates pointer arithmetic in a realistic scenario: array processing with pointer traversal.

Program: Pointer Arithmetic Demo

c

/** * pointer_arithmetic_demo.c * Demonstrates pointer arithmetic operations. * Includes array traversal, element counting, and pointer comparison. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define MAX_LINE_LEN 100 #define ARRAY_SIZE 10 /* Function prototypes */ void demonstrateIncrementDecrement(void); void demonstrateAdditionSubtraction(void); void demonstratePointerDifference(void); void demonstratePointerComparison(void); void demonstrateArrayTraversal(void); void demonstrateCountOddEven(void); void demonstratePointerArithmeticOnArray(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; printf("=== Pointer Arithmetic Demo ===\n"); while (1) { printf("\n--- Menu ---\n"); printf("1. Increment / Decrement\n"); printf("2. Addition / Subtraction\n"); printf("3. Pointer Difference\n"); printf("4. Pointer Comparison\n"); printf("5. Array Traversal with Pointer\n"); printf("6. Count Odd/Even Elements\n"); printf("7. Pointer Arithmetic on Array\n"); printf("8. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: demonstrateIncrementDecrement(); break; case 2: demonstrateAdditionSubtraction(); break; case 3: demonstratePointerDifference(); break; case 4: demonstratePointerComparison(); break; case 5: demonstrateArrayTraversal(); break; case 6: demonstrateCountOddEven(); break; case 7: demonstratePointerArithmeticOnArray(); break; case 8: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Demonstrates increment and decrement */ void demonstrateIncrementDecrement(void) { printf("\n=== Increment / Decrement ===\n"); int arr[] = {10, 20, 30, 40, 50}; int *ptr = arr; printf("Array: [10, 20, 30, 40, 50]\n"); printf("ptr = %p, *ptr = %d\n", (void*)ptr, *ptr); ptr++; printf("ptr++ -> %p, *ptr = %d\n", (void*)ptr, *ptr); ptr++; printf("ptr++ -> %p, *ptr = %d\n", (void*)ptr, *ptr); ptr--; printf("ptr-- -> %p, *ptr = %d\n", (void*)ptr, *ptr); /* Different types advance differently */ char charArr[] = "Hello"; char *charPtr = charArr; printf("\nchar pointer: %p, *charPtr = '%c'\n", (void*)charPtr, *charPtr); charPtr++; printf("charPtr++ -> %p, *charPtr = '%c' (advanced 1 byte)\n", (void*)charPtr, *charPtr); double doubleArr[] = {1.1, 2.2, 3.3}; double *doublePtr = doubleArr; printf("\ndouble pointer: %p, *doublePtr = %.1f\n", (void*)doublePtr, *doublePtr); doublePtr++; printf("doublePtr++ -> %p, *doublePtr = %.1f (advanced 8 bytes)\n", (void*)doublePtr, *doublePtr); } /* Demonstrates addition and subtraction */ void demonstrateAdditionSubtraction(void) { printf("\n=== Addition / Subtraction ===\n"); int arr[] = {10, 20, 30, 40, 50}; int *ptr = arr; printf("Array: [10, 20, 30, 40, 50]\n"); printf("Initial: ptr = %p, *ptr = %d\n", (void*)ptr, *ptr); ptr = ptr + 2; printf("ptr + 2: %p, *ptr = %d\n", (void*)ptr, *ptr); ptr += 2; printf("ptr += 2: %p, *ptr = %d\n", (void*)ptr, *ptr); ptr = ptr - 3; printf("ptr - 3: %p, *ptr = %d\n", (void*)ptr, *ptr); ptr -= 1; printf("ptr -= 1: %p, *ptr = %d\n", (void*)ptr, *ptr); /* Equivalent expressions */ printf("\nEquivalent expressions:\n"); ptr = arr; printf("arr[3] = %d\n", arr[3]); printf("*(arr + 3) = %d\n", *(arr + 3)); printf("*(ptr + 3) = %d\n", *(ptr + 3)); printf("ptr[3] = %d\n", ptr[3]); } /* Demonstrates pointer difference */ void demonstratePointerDifference(void) { printf("\n=== Pointer Difference ===\n"); int arr[] = {10, 20, 30, 40, 50}; int *ptr1 = &arr[1]; int *ptr2 = &arr[4]; printf("Array: [10, 20, 30, 40, 50]\n"); printf("ptr1 points to arr[1] = %d\n", *ptr1); printf("ptr2 points to arr[4] = %d\n", *ptr2); ptrdiff_t diff = ptr2 - ptr1; printf("ptr2 - ptr1 = %td elements\n", diff); diff = ptr1 - ptr2; printf("ptr1 - ptr2 = %td elements\n", diff); /* Byte difference vs element difference */ printf("\nByte addresses:\n"); printf("&arr[1] = %p\n", (void*)&arr[1]); printf("&arr[4] = %p\n", (void*)&arr[4]); printf("Byte difference = %td bytes\n", (char*)&arr[4] - (char*)&arr[1]); printf("Element difference = %td elements\n", ptr2 - ptr1); } /* Demonstrates pointer comparison */ void demonstratePointerComparison(void) { printf("\n=== Pointer Comparison ===\n"); int arr[] = {10, 20, 30, 40, 50}; int *ptr1 = &arr[0]; int *ptr2 = &arr[3]; int *ptr3 = &arr[0]; printf("ptr1 = %p (arr[0])\n", (void*)ptr1); printf("ptr2 = %p (arr[3])\n", (void*)ptr2); printf("ptr3 = %p (arr[0])\n", (void*)ptr3); if (ptr1 < ptr2) { printf("ptr1 < ptr2: true\n"); } if (ptr1 == ptr3) { printf("ptr1 == ptr3: true\n"); } if (ptr1 != ptr2) { printf("ptr1 != ptr2: true\n"); } if (ptr2 > ptr1) { printf("ptr2 > ptr1: true\n"); } /* Comparison with NULL */ int *nullPtr = NULL; if (nullPtr == NULL) { printf("\nnullPtr == NULL: true\n"); } } /* Demonstrates array traversal with pointer */ void demonstrateArrayTraversal(void) { printf("\n=== Array Traversal with Pointer ===\n"); int arr[] = {1, 2, 3, 4, 5}; int n = sizeof(arr) / sizeof(arr[0]); printf("Method 1: Using pointer arithmetic\n"); for (int *p = arr; p < arr + n; p++) { printf("%d ", *p); } printf("\n"); printf("Method 2: Using pointer indexing\n"); int *ptr = arr; for (int i = 0; i < n; i++) { printf("%d ", ptr[i]); } printf("\n"); printf("Method 3: Using array indexing\n"); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); printf("Method 4: Reverse traversal\n"); for (int *p = arr + n - 1; p >= arr; p--) { printf("%d ", *p); } printf("\n"); } /* Demonstrates counting odd/even using pointer */ void demonstrateCountOddEven(void) { printf("\n=== Count Odd/Even Elements ===\n"); int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int n = sizeof(arr) / sizeof(arr[0]); int *ptr = arr; int countEven = 0; int countOdd = 0; for (int i = 0; i < n; i++) { if (*ptr % 2 == 0) { countEven++; } else { countOdd++; } ptr++; /* Move to next element */ } printf("Array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n"); printf("Even elements: %d\n", countEven); printf("Odd elements: %d\n", countOdd); } /* Demonstrates pointer arithmetic on array */ void demonstratePointerArithmeticOnArray(void) { printf("\n=== Pointer Arithmetic on Array ===\n"); int arr[] = {1, 2, 3, 4, 5}; int n = sizeof(arr) / sizeof(arr[0]); printf("Array: [1, 2, 3, 4, 5]\n"); /* Array name as pointer */ printf("arr = %p\n", (void*)arr); printf("&arr[0] = %p\n", (void*)&arr[0]); printf("*arr = %d\n", *arr); /* Accessing elements using array name as pointer */ printf("\nUsing array name as pointer:\n"); for (int i = 0; i < n; i++) { printf("*(arr + %d) = %d\n", i, *(arr + i)); } /* Pointer to array */ int *ptr = arr; printf("\nUsing pointer variable:\n"); for (int i = 0; i < n; i++) { printf("*(ptr + %d) = %d\n", i, *(ptr + i)); } }

Explanation of Pointer Arithmetic Usage

  1. Increment/Decrement: Moves pointer by sizeof(type) bytes.
  2. Addition/Subtraction: Moves pointer by n × sizeof(type) bytes.
  3. Pointer Difference: Returns element count between pointers.
  4. Comparison: Compares memory addresses.
  5. Array Traversal: Multiple methods using pointers.
  6. Odd/Even Counting: Uses pointer to traverse array.
  7. Array Name as Pointer: Demonstrates arr vs &arr[0].

Expected Output (sample run)

text

=== Pointer Arithmetic Demo === --- Menu --- 1. Increment / Decrement 2. Addition / Subtraction 3. Pointer Difference 4. Pointer Comparison 5. Array Traversal with Pointer 6. Count Odd/Even Elements 7. Pointer Arithmetic on Array 8. Exit Enter choice: 1 === Increment / Decrement === Array: [10, 20, 30, 40, 50] ptr = 0x7ffc1000, *ptr = 10 ptr++ -> 0x7ffc1004, *ptr = 20 ptr++ -> 0x7ffc1008, *ptr = 30 ptr-- -> 0x7ffc1004, *ptr = 20 char pointer: 0x7ffc0ffb, *charPtr = 'H' charPtr++ -> 0x7ffc0ffc, *charPtr = 'e' (advanced 1 byte) double pointer: 0x7ffc0fe0, *doublePtr = 1.1 doublePtr++ -> 0x7ffc0fe8, *doublePtr = 2.2 (advanced 8 bytes) --- Menu --- ... Enter choice: 3 === Pointer Difference === Array: [10, 20, 30, 40, 50] ptr1 points to arr[1] = 20 ptr2 points to arr[4] = 50 ptr2 - ptr1 = 3 elements ptr1 - ptr2 = -3 elements Byte addresses: &arr[1] = 0x7ffc1004 &arr[4] = 0x7ffc1010 Byte difference = 12 bytes Element difference = 3 elements

Common Pitfalls, Edge Cases & Memory Considerations

1. Pointer Arithmetic Out of Bounds

Accessing beyond array bounds is undefined behavior.

c

int arr[5]; int *p = arr + 5; /* One past the end (valid for comparison) */ int value = *p; /* BUG: dereferencing out of bounds */

Solution: Keep pointers within [arr, arr + n - 1] for dereferencing.

2. Subtracting Pointers from Different Arrays

Subtracting pointers from different arrays is undefined behavior.

c

int arr1[5], arr2[5]; int diff = arr2 - arr1; /* BUG: undefined behavior */

Solution: Only subtract pointers that point to the same array.

3. Pointer Arithmetic on void*

Standard C does not allow arithmetic on void* (GCC allows as extension).

c

void *ptr = malloc(10); ptr++; /* Error in standard C */

Solution: Cast to a typed pointer: ((char*)ptr)++;

4. Adding Two Pointers

Adding two pointers is meaningless and not allowed.

c

int *ptr1, *ptr2; int *result = ptr1 + ptr2; /* Error: invalid operands */

Solution: Only add integers to pointers, not pointers to pointers.

5. Using sizeof on Pointer After Arithmetic

sizeof on a pointer gives pointer size, not the size of the pointed-to data.

c

int arr[5]; int *p = arr; sizeof(p); /* 8 (pointer size), not 4 (int size) */ sizeof(*p); /* 4 (int size) */

Solution: Use sizeof(*p) for element size.

6. Pointer Arithmetic on Function Pointers

Pointer arithmetic is not allowed on function pointers.

c

void (*funcPtr)(void); funcPtr++; /* Error: arithmetic on function pointer */

Solution: Do not perform arithmetic on function pointers.

7. Incrementing Beyond Array End

Incrementing past the array end in a loop can cause out-of-bounds access.

c

int arr[5]; int *p = arr; for (int i = 0; i <= 5; i++) { *p = i; /* BUG: accesses arr[5] on last iteration */ p++; }

Solution: Use i < 5 (strict inequality).

8. Decrementing Before Array Start

Decrementing before the array start is undefined behavior.

c

int arr[5]; int *p = arr; p--; /* BUG: before array start */

Solution: Check pointer is greater than array start before decrementing.

9. Alignment Issues

Pointer arithmetic assumes proper alignment. Casting to misaligned pointers can cause issues.

c

char c = 'A'; int *p = (int*)&c; /* Possible alignment issue */

Solution: Avoid casting to misaligned pointer types.

10. Memory Considerations

  • Pointer arithmetic does not allocate or free memory.
  • It works on addresses within allocated memory.
  • For dynamically allocated memory, ensure the pointer stays within the allocated block.
  • Using ptrdiff_t (from <stddef.h>) for pointer differences avoids overflow.

Summary & Best Practices

Pointer arithmetic is a powerful feature that enables efficient array processing and memory navigation. It operates based on the size of the pointed-to data type.

Key Takeaways

  • Pointer arithmetic moves by sizeof(type) bytes per unit.
  • Valid operations: ++, --, + n, - n, pointer difference, comparison.
  • Invalid operations: adding pointers, multiplying pointers, arithmetic on void*.
  • Pointer difference returns element count (use ptrdiff_t).
  • Array name acts as a constant pointer to first element.
  • Keep pointers within array bounds.

Best Practices

  1. Keep pointer arithmetic within array bounds.
  2. Use ptrdiff_t for pointer differences.
  3. Only subtract pointers from the same array.
  4. Cast void* before arithmetic.
  5. Do not perform arithmetic on function pointers.
  6. Use sizeof(*ptr) for element size.
  7. Prefer array indexing for readability when performance is not critical.
  8. Use pointer arithmetic for performance-critical loops.
  9. Document pointer arithmetic with comments for clarity.
  10. Test edge cases: first element, last element, empty array.

By mastering pointer arithmetic, you’ll write efficient C code for array processing, dynamic memory management, and low-level programming.

Leave a Reply

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