Modifying and Working with Pointers
Pointer Modification Basics
Pointers can be modified in several ways:
- Changing the address they store
- Using pointer arithmetic (within the same array object)
- Applying type casting (with strict rules)
- Leveraging their close relationship with arrays (array-to-pointer decay)
Pointer Arithmetic
Pointer arithmetic automatically accounts for the size of the pointed-to type. For example, incrementing an int* advances by sizeof(int).
The result of subtracting two pointers to the same array is of type ptrdiff_t (a signed integer type).
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr; // Points to arr[0]
ptr++; // Now points to arr[1]
ptr += 2; // Now points to arr[3]
ptr--; // Now points to arr[2]
// Difference between pointers (elements, not bytes)
#include <cstddef> // for ptrdiff_t
ptrdiff_t diff = ptr - arr; // Yields 2
Pointer Modification Examples
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
// Changing pointer addresses
int a = 5, b = 10;
int* ptr = &a;
cout << *ptr << '\n'; // 5
ptr = &b;
cout << *ptr << '\n'; // 10
// Array traversal with a pointer
double numbers[3] = {1.1, 2.2, 3.3};
double* numPtr = numbers; // same as &numbers[0]
for (int i = 0; i < 3; i++) {
cout << "Address: " << static_cast<const void*>(numPtr)
<< " Value: " << *numPtr << '\n';
numPtr++; // safe: stays within the same array
}
// Type punning using a char* (allowed aliasing)
int value = 0x41424344; // bytes of 'A','B','C','D' in big-endian order
char* bytePtr = reinterpret_cast<char*>(&value);
for (int i = 0; i < 4; i++) {
cout << *bytePtr << ' ';
bytePtr++;
}
cout << '\n';
return 0;
}
5 10 Address: 0x7ffd4d5a5a40 Value: 1.1 Address: 0x7ffd4d5a5a48 Value: 2.2 Address: 0x7ffd4d5a5a50 Value: 3.3 D C B A
Advanced Pointer Operations
Pointer to Pointer: Allows multiple levels of indirection
Function Pointers: Store the address of functions
Smart Pointers: Modern C++ resource management (unique_ptr, shared_ptr, weak_ptr)
Pointer Safety: Guidelines to prevent errors such as memory leaks and dangling pointers
// Pointer to pointer
int x = 42;
int* p = &x;
int** pp = &p;
**pp = 99; // modifies x
// Function pointer
int add(int a, int b) { return a + b; }
int (*funcPtr)(int, int) = &add;
std::cout << "Function result: " << funcPtr(3, 4) << '\n';
// Smart pointer example
#include <memory>
auto smartPtr = std::make_unique<int>(42); // C++14+
std::cout << *smartPtr << '\n';
Function result: 7 42
Pointer Safety Checklist
- Initialize pointers; prefer nullptr over 0/NULL.
- Only do arithmetic within a single array (or one-past-the-end). Never dereference one-past.
- Do not compare or subtract pointers from different objects.
- After delete, do not reuse the pointer (consider setting it to nullptr).
- Prefer smart pointers and standard containers to manage lifetime automatically.