Pointers
Pointers Deep Dive
Section titled “Pointers Deep Dive”Pointers are one of the most powerful and dangerous features of C++. This chapter provides an in-depth look at pointers, their types, and how to use them safely.
What is a Pointer?
Section titled “What is a Pointer?”A pointer is a variable that stores the memory address of another variable:
┌─────────────────────────────────────────────────────────────┐│ Pointer Concept │├─────────────────────────────────────────────────────────────┤│ ││ int variable: int* pointer: ││ ┌─────────┐ ┌─────────┐ ││ │ 42 │ │ 0x1000 │──────┐ ││ └─────────┘ └─────────┘ │ ││ 0x1000 │ │ ││ ▼ ▼ ││ ┌─────────┐ ┌─────────┐ ││ │ 42 │◄───┘ (int) │ ││ └─────────┘ ││ Memory: 0x1000 ││ │└─────────────────────────────────────────────────────────────┘Declaring Pointers
Section titled “Declaring Pointers”#include <iostream>
int main() { int num = 42;
// Declare pointer to int int* ptr = # // & gets the address
// Print values std::cout << "Value: " << num << "\n"; std::cout << "Address: " << &num << "\n"; std::cout << "Pointer value: " << ptr << "\n"; std::cout << "Dereferenced: " << *ptr << "\n"; // * dereferences
return 0;}Pointer Types
Section titled “Pointer Types”Pointers to Different Types
Section titled “Pointers to Different Types”int* intPtr; // Pointer to intdouble* doublePtr; // Pointer to doublechar* charPtr; // Pointer to charvoid* voidPtr; // Pointer to anything (raw)const Pointers
Section titled “const Pointers”int value = 42;
// Pointer to constconst int* ptr1 = &value;// *ptr1 = 100; // Error: can't modify through ptr1ptr1 = nullptr; // OK: can change pointer
// Const pointerint* const ptr2 = &value;*ptr2 = 100; // OK: can modify value// ptr2 = nullptr; // Error: can't change pointer
// Const pointer to constconst int* const ptr3 = &value;// Both pointer and value are constPointer Arithmetic
Section titled “Pointer Arithmetic”int arr[] = {10, 20, 30, 40, 50};int* ptr = arr; // Points to arr[0]
// Pointer arithmeticptr++; // Now points to arr[1]ptr += 2; // Now points to arr[3]
// Access elementint val = *(ptr + 1); // arr[4]int val2 = *ptr; // arr[3]
// Differenceptrdiff_t diff = &arr[4] - &arr[0]; // 4Arrays and Pointers
Section titled “Arrays and Pointers”Arrays and pointers are closely related:
int arr[] = {1, 2, 3, 4, 5};int* ptr = arr; // Array decays to pointer
// Both work the samearr[0] == *(arr + 0) == *ptr == ptr[0]
// Passing arrays to functionsvoid processArray(int* arr, size_t size) {}
// Both declarations equivalentvoid processArray(int arr[], size_t size) {}void processArray(int* arr, size_t size) {}Pointers to Objects
Section titled “Pointers to Objects”class Person {private: std::string name;
public: Person(const std::string& n) : name(n) {} void greet() { std::cout << "Hello, I'm " << name << "\n"; }};
int main() { Person obj("Alice"); Person* ptr = &obj;
// Access members through pointer ptr->greet(); // Same as (*ptr).greet()}Dynamic Memory Allocation
Section titled “Dynamic Memory Allocation”#include <cstdlib>
int main() { // Allocate single int int* ptr = new int(42); std::cout << *ptr << "\n"; delete ptr; // Free memory ptr = nullptr; // Avoid dangling pointer
// Allocate array int* arr = new int[10]; delete[] arr; // Free array
// Modern C++ prefers smart pointers}Dangling Pointers
Section titled “Dangling Pointers”A dangling pointer points to freed memory:
int* ptr = new int(42);delete ptr;// ptr is now dangling - don't use it!
// Set to null after deleteptr = nullptr;
// Check before usingif (ptr != nullptr) { // Safe to use}Null Pointers
Section titled “Null Pointers”int* nullPtr = nullptr; // C++11 nullptr
// Check nullif (ptr != nullptr) {}
// Use in conditions (converts to false)if (ptr) {} // False if nullPointers to Pointers
Section titled “Pointers to Pointers”int value = 42;int* ptr1 = &value;int** ptr2 = &ptr1; // Pointer to pointer
**ptr2 = 100; // Changes value to 100Complete Example: Dynamic Array
Section titled “Complete Example: Dynamic Array”#include <iostream>
class DynamicArray {private: int* data; size_t size;
public: DynamicArray(size_t s) : size(s) { data = new int[size](); // Value-initialized to 0 }
~DynamicArray() { delete[] data; // Free memory }
int& operator[](size_t index) { return data[index]; }
size_t getSize() const { return size; }
// Prevent copying (Rule of 5) DynamicArray(const DynamicArray&) = delete; DynamicArray& operator=(const DynamicArray&) = delete;};
int main() { DynamicArray arr(5);
for (size_t i = 0; i < arr.getSize(); i++) { arr[i] = (i + 1) * 10; }
for (size_t i = 0; i < arr.getSize(); i++) { std::cout << arr[i] << " "; } // Output: 10 20 30 40 50
return 0;}Common Pointer Mistakes
Section titled “Common Pointer Mistakes”// 1. Not initializingint* ptr; // Uninitialized - garbage value!
// 2. Memory leakint* ptr = new int(42);// forgot delete
// 3. Dangling pointerint* ptr = new int(42);delete ptr;std::cout << *ptr; // Undefined behavior!
// 4. Double deleteint* ptr = new int(42);delete ptr;delete ptr; // Undefined behavior!
// 5. Wrong deleteint* ptr = new int[10];delete ptr; // Wrong! Should be delete[]Best Practices
Section titled “Best Practices”- Prefer smart pointers over raw pointers
- Always initialize pointers
- Check for null before dereferencing
- Set pointer to null after delete
- Match new with delete, new[] with delete[]
Key Takeaways
Section titled “Key Takeaways”- Pointers store memory addresses
- Use
*to dereference pointers - Use
&to get address of a variable - Pointer arithmetic works on array elements
- Always delete dynamically allocated memory
- Prefer smart pointers in modern C++
Next Steps
Section titled “Next Steps”Let’s learn about references and how they differ from pointers.
Next Chapter: 20_references.md - References and Lifetime