Pointers and Dynamic Memory: The Power Behind C++

What Are Pointers?

Imagine your computer's memory as a huge apartment building. Each apartment (memory location) has a unique address. A pointer is like having someone's address written on a piece of paper - it tells you WHERE to find them, not WHO they are!

Understanding Memory Addresses

Every variable lives somewhere in memory. The & operator (address-of) tells you WHERE it lives!

Variables and Their Addresses Variable int age = 25; Value: 25 &age Address: 0x7ffe5c Pointer int* ptr = &age; Value: 0x7ffe5c *ptr Points to: 25

Pointer Declaration and Usage

graph TD A[Pointer Operations] --> B[Declaration: type* name] A --> C[Address-of: &variable] A --> D[Dereference: *pointer] B --> E["int* ptr;"] C --> F["ptr = &x;"] D --> G["value = *ptr;"] E --> H[Creates pointer variable] F --> I[Gets address of x] G --> J[Gets value at address]

Basic Pointer Examples

// Pointer basics
int x = 42;           // Regular variable
int* ptr;             // Pointer to int (currently uninitialized - dangerous!)
ptr = &x;             // ptr now holds the address of x

cout << "x = " << x << endl;              // Prints: 42
cout << "&x = " << &x << endl;            // Prints: address (e.g., 0x7ffe5c)
cout << "ptr = " << ptr << endl;          // Prints: same address
cout << "*ptr = " << *ptr << endl;        // Prints: 42

// Modifying through pointer
*ptr = 100;           // Changes the value at the address ptr points to
cout << "x = " << x << endl;              // Prints: 100 (x was changed!)

// Multiple pointers to same variable
int* ptr2 = &x;
*ptr2 = 200;
cout << "*ptr = " << *ptr << endl;        // Prints: 200 (both see the change)

Pointer Arithmetic: Navigation in Memory

Pointer arithmetic is like navigating apartments - adding 1 to a pointer moves to the next apartment of that size!

Pointer Arithmetic Examples

// Array and pointer relationship
int arr[] = {10, 20, 30, 40, 50};
int* ptr = arr;  // ptr points to first element

// Different ways to access elements
cout << arr[0] << endl;      // 10
cout << *ptr << endl;        // 10 (same as arr[0])
cout << *(ptr + 2) << endl;  // 30 (same as arr[2])

// Moving through array with pointer
for (int i = 0; i < 5; i++) {
    cout << *ptr << " ";     // Print current element
    ptr++;                   // Move to next element
}

// Pointer arithmetic with different types
char str[] = "Hello";
char* cptr = str;
cptr++;  // Moves 1 byte (size of char)

double values[] = {1.1, 2.2, 3.3};
double* dptr = values;
dptr++;  // Moves 8 bytes (size of double)

Dynamic Memory Allocation: Creating Space at Runtime

Static memory is like assigned seating - you know exactly how many seats you need beforehand. Dynamic memory is like a restaurant that can add tables as guests arrive!

Static vs Dynamic Memory Stack (Static) int x = 10; int arr[5]; ✓ Size known at compile ✓ Automatic cleanup ✗ Fixed size ✗ Limited space Heap (Dynamic) int* p = new int; int* arr = new int[n]; ✓ Size at runtime ✓ Flexible size ✓ Large space ✗ Manual cleanup!

The new and delete Operators

stateDiagram-v2 [*] --> Dynamic_Memory_Management Dynamic_Memory_Management --> Allocation Dynamic_Memory_Management --> Deallocation state Allocation { [*] --> Choose_kind Choose_kind --> Single_object: use new type Choose_kind --> Array: use new with size N Single_object --> Example_single: int pointer = new int Array --> Example_array: int array of size 10 } state Deallocation { [*] --> Choose_delete Choose_delete --> Delete_single: delete ptr Choose_delete --> Delete_array: delete array Delete_single --> Example_del_single: delete ptr Delete_array --> Example_del_array: delete array }

Dynamic Memory Examples

// Single variable allocation
int* ptr = new int;        // Allocate space for one int
*ptr = 42;                 // Use it like normal
cout << *ptr << endl;      // Prints: 42
delete ptr;                // FREE THE MEMORY!

// Array allocation
int size;
cout << "How many numbers? ";
cin >> size;

int* numbers = new int[size];  // Dynamic array!

// Use the array
for (int i = 0; i < size; i++) {
    numbers[i] = i * 10;
}

// Print array
for (int i = 0; i < size; i++) {
    cout << numbers[i] << " ";
}

delete[] numbers;          // FREE THE ARRAY! Note the []

// Common mistake - memory leak!
int* leak = new int;
leak = new int;            // Old memory is lost forever!
// Always delete before reassigning!

Memory Leaks: The Silent Killer

Memory leaks are like borrowing books from a library and never returning them - eventually, there are no books left for anyone!

Pointer Safety Rules

Pointer Safety Guidelines Initialize Pointers ✗ int* ptr; ✓ int* ptr = nullptr; Check Before Use if (ptr != nullptr) { *ptr = 10; } Delete = Set nullptr delete ptr; ptr = nullptr; Match new/delete new → delete new[] → delete[] Don't Delete Twice ✗ delete ptr; ✗ delete ptr; // Crash!

Dynamic Arrays in Practice

// Dynamic array example - Grade management system
class GradeManager {
private:
    double* grades;
    int capacity;
    int size;
    
public:
    GradeManager(int initialCapacity = 10) {
        capacity = initialCapacity;
        size = 0;
        grades = new double[capacity];
    }
    
    ~GradeManager() {
        delete[] grades;  // Destructor cleans up!
    }
    
    void addGrade(double grade) {
        if (size == capacity) {
            // Need to grow the array
            capacity *= 2;
            double* newGrades = new double[capacity];
            
            // Copy old data
            for (int i = 0; i < size; i++) {
                newGrades[i] = grades[i];
            }
            
            // Delete old array
            delete[] grades;
            grades = newGrades;
        }
        
        grades[size++] = grade;
    }
    
    double getAverage() {
        if (size == 0) return 0;
        
        double sum = 0;
        for (int i = 0; i < size; i++) {
            sum += grades[i];
        }
        return sum / size;
    }
};

Pointers and Functions

Pointers let functions modify variables directly and work with arrays efficiently!

Functions with Pointers

// Passing pointers to modify variables
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// Array functions always receive pointers
int sumArray(int* arr, int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];  // or *(arr + i)
    }
    return sum;
}

// Returning dynamically allocated memory
int* createArray(int size) {
    int* arr = new int[size];
    for (int i = 0; i < size; i++) {
        arr[i] = i * i;
    }
    return arr;  // Caller must delete[]!
}

// Safe string copy
void safeCopy(char* dest, const char* src, int maxLen) {
    int i = 0;
    while (src[i] != '\0' && i < maxLen - 1) {
        dest[i] = src[i];
        i++;
    }
    dest[i] = '\0';
}

Practice Exercise: Dynamic Student Records

Build a Dynamic Student Database

Create a program that manages student records with dynamic memory:

  1. Store student names and grades
  2. Allow adding/removing students
  3. Resize arrays as needed
  4. Calculate class statistics
#include <iostream>
#include <string>
using namespace std;

struct Student {
    string name;
    double grade;
};

class StudentDatabase {
private:
    Student* students;
    int capacity;
    int count;
    
public:
    StudentDatabase() {
        // TODO: Initialize with capacity of 5
    }
    
    ~StudentDatabase() {
        // TODO: Clean up memory
    }
    
    void addStudent(string name, double grade) {
        // TODO: Add student, resize if needed
    }
    
    void displayAll() {
        // TODO: Show all students
    }
    
    double getClassAverage() {
        // TODO: Calculate average grade
    }
};
Constructor Hint
StudentDatabase() {
    capacity = 5;
    count = 0;
    students = new Student[capacity];
}

Common Pointer Pitfalls

graph TD A[Pointer Pitfalls] --> B[Dangling Pointer] A --> C[Memory Leak] A --> D[Buffer Overflow] A --> E[Double Delete] B --> F[Points to deleted memory] C --> G[Forget to delete] D --> H[Write past array end] E --> I[Delete same memory twice]

Smart Pointers Preview (Modern C++)

Modern C++ provides smart pointers that automatically manage memory - like having a responsible assistant!

Evolution to Smart Pointers Raw Pointer Manual management Error prone Smart Pointer Automatic cleanup Exception safe unique_ptr shared_ptr

Challenge Exercise: Dynamic Matrix Operations

Advanced Pointer Challenge

Create a Matrix class using dynamic 2D arrays:

  1. Dynamic rows and columns
  2. Matrix addition and multiplication
  3. Proper memory management
  4. Copy constructor and assignment operator
2D Dynamic Array Hint
// Allocate 2D array
int** matrix = new int*[rows];
for(int i = 0; i < rows; i++) {
    matrix[i] = new int[cols];
}

// Don't forget to delete!
for(int i = 0; i < rows; i++) {
    delete[] matrix[i];
}
delete[] matrix;

Key Takeaways

graph LR A[Master Pointers] --> B[Control Memory] B --> C[Build Efficient Programs] C --> D[Create Dynamic Applications] D --> E[Professional C++ Developer!]