07

Module 7: Pointers and References

Chapter 7 • Intermediate

55 min

Pointers and References

Pointers and references are powerful features in C++ that allow you to work directly with memory addresses. They're essential for efficient memory management and advanced programming.

What are Pointers?

A pointer is a variable that stores the memory address of another variable. Instead of storing a value, it stores where the value is located in memory.

Key Concepts:

  • Address: Location in memory where data is stored
  • Pointer: Variable that holds an address
  • Dereference: Accessing the value at an address

Pointer Declaration

Syntax:

cpp.js
type* pointerName;

Example:

cpp.js
int* ptr;        // Pointer to int
double* dptr;    // Pointer to double
char* cptr;      // Pointer to char

Address-of Operator (&)

Gets the memory address of a variable:

cpp.js
int num = 10;
int* ptr = #  // ptr now holds address of num

Dereference Operator (*)

Accesses the value at the address stored in a pointer:

cpp.js
int num = 10;
int* ptr = #
cout << *ptr << endl;  // Prints 10 (value at address)

Basic Pointer Operations

Example:

cpp.js
int num = 10;
int* ptr = &num;

cout << "Value of num: " << num << endl;        // 10
cout << "Address of num: " << &num << endl;     // Memory address
cout << "Value of ptr: " << ptr << endl;        // Same address
cout << "Value at ptr: " << *ptr << endl;       // 10

Modifying Values Through Pointers

Example:

cpp.js
int num = 10;
int* ptr = &num;

*ptr = 20;  // Changes num to 20
cout << num << endl;  // 20

Null Pointers

Pointers that don't point to anything:

cpp.js
int* ptr = nullptr;  // C++11 (preferred)
int* ptr2 = NULL;     // C-style
int* ptr3 = 0;        // Also valid

Always initialize pointers! Uninitialized pointers contain garbage addresses.

Pointer Arithmetic

Pointers can be incremented/decremented to move to adjacent memory locations:

cpp.js
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr;  // Points to first element

cout << *ptr << endl;      // 10
ptr++;                      // Move to next element
cout << *ptr << endl;      // 20
ptr += 2;                   // Move 2 elements forward
cout << *ptr << endl;      // 40

Important: Pointer arithmetic depends on the size of the data type.

Pointers and Arrays

Arrays and pointers are closely related:

cpp.js
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr;  // Array name is pointer to first element

// These are equivalent:
cout << arr[0] << endl;    // 10
cout << *arr << endl;      // 10
cout << ptr[0] << endl;    // 10
cout << *ptr << endl;      // 10

Pointers as Function Parameters

Passing pointers allows functions to modify original values:

cpp.js
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(&x, &y);  // Pass addresses
    cout << x << " " << y << endl;  // 20 10
    return 0;
}

Dynamic Memory Allocation

new Operator

Allocates memory at runtime:

cpp.js
int* ptr = new int;        // Allocate single integer
*ptr = 10;

int* arr = new int[5];     // Allocate array of 5 integers
arr[0] = 10;
arr[1] = 20;

delete Operator

Frees allocated memory:

cpp.js
int* ptr = new int;
*ptr = 10;
delete ptr;  // Free single variable
ptr = nullptr;  // Good practice

int* arr = new int[5];
delete[] arr;  // Free array (use delete[])
arr = nullptr;

Important Rules:

  • Every new must have a corresponding delete
  • Use delete for single variables, delete[] for arrays
  • Set pointer to nullptr after deletion
  • Never delete the same pointer twice

Memory Leaks

Memory leaks occur when allocated memory is not freed:

cpp.js
void leak() {
    int* ptr = new int[1000];
    // Forgot to delete - memory leak!
}

Prevention:

  • Always match new with delete
  • Use smart pointers (we'll cover later)
  • Use RAII (Resource Acquisition Is Initialization)

What are References?

A reference is an alias (another name) for an existing variable. Unlike pointers, references must be initialized and cannot be reassigned.

Key Differences from Pointers:

  • Must be initialized when declared
  • Cannot be reassigned to point to another variable
  • No need for dereference operator
  • Cannot be null

Reference Declaration

Syntax:

cpp.js
type& refName = variable;

Example:

cpp.js
int num = 10;
int& ref = num;  // ref is alias for num

ref = 20;        // Changes num to 20
cout << num << endl;  // 20

References as Function Parameters

References allow functions to modify original values without pointers:

cpp.js
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(x, y);  // Pass variables directly
    cout << x << " " << y << endl;  // 20 10
    return 0;
}

Benefits:

  • Cleaner syntax (no & when calling)
  • No need to check for null
  • Cannot be reassigned accidentally

Const References

Prevent modification while avoiding copying:

cpp.js
void print(const int& value) {
    cout << value << endl;
    // value = 10;  // Error: cannot modify const reference
}

Use const references for:

  • Large objects (avoid copying)
  • Parameters that shouldn't be modified
  • Read-only access

Pointers vs References

FeaturePointerReference
**Declaration**`int* ptr;``int& ref = var;`
**Initialization**Can be nullMust initialize
**Reassignment**Can changeCannot change
**Null value**Can be nullCannot be null
**Dereference**Need `*`Automatic
**Address**Has own addressSame as variable

When to use:

  • Pointers: Dynamic memory, optional parameters, arrays, C compatibility
  • References: Function parameters, return values, aliases

Pointer to Pointer

Pointers can point to other pointers:

cpp.js
int num = 10;
int* ptr = &num;
int** pptr = &ptr;  // Pointer to pointer

cout << num << endl;      // 10
cout << *ptr << endl;    // 10
cout << **pptr << endl;  // 10

Function Pointers

Pointers that point to functions:

cpp.js
int add(int a, int b) {
    return a + b;
}

int main() {
    int (*funcPtr)(int, int) = add;
    int result = funcPtr(5, 3);  // Calls add(5, 3)
    return 0;
}

Common Patterns

Pattern 1: Swap Function

cpp.js
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

Pattern 2: Array Parameter

cpp.js
void printArray(int* arr, int size) {
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
}

Pattern 3: Return by Reference

cpp.js
int& getElement(int* arr, int index) {
    return arr[index];
}

// Usage
int arr[5] = {1, 2, 3, 4, 5};
getElement(arr, 2) = 99;  // Modifies arr[2]

Best Practices

  1. Always initialize pointers (use nullptr)
  2. Check for null before dereferencing pointers
  3. Match new with delete (and new[] with delete[])
  4. Set pointers to nullptr after deletion
  5. Use references for function parameters when possible
  6. Use const references for read-only parameters
  7. Avoid raw pointers when smart pointers are available
  8. Prefer references over pointers when null isn't needed

Common Mistakes

  • ❌ Dereferencing null or uninitialized pointers
  • ❌ Memory leaks (forgetting to delete)
  • ❌ Double deletion (deleting same pointer twice)
  • ❌ Using deleted memory
  • ❌ Array bounds violations with pointers
  • ❌ Mixing up * (dereference) and & (address-of)
  • ❌ Trying to reassign references

Next Module

In Module 8, we'll learn about Object-Oriented Programming - how to create classes and objects to model real-world entities!

Hands-on Examples

Basic Pointer Operations

#include <iostream>
using namespace std;

int main() {
    int num = 10;
    int* ptr = &num;  // ptr stores address of num
    
    cout << "Value of num: " << num << endl;
    cout << "Address of num: " << &num << endl;
    cout << "Value of ptr (address): " << ptr << endl;
    cout << "Value at ptr: " << *ptr << endl;
    
    // Modifying through pointer
    *ptr = 20;
    cout << "After *ptr = 20, num = " << num << endl;
    
    // Pointer arithmetic
    int arr[5] = {10, 20, 30, 40, 50};
    int* arrPtr = arr;
    
    cout << "\nArray via pointer:" << endl;
    for (int i = 0; i < 5; i++) {
        cout << "arr[" << i << "] = " << *(arrPtr + i) << endl;
    }
    
    return 0;
}

Pointers store memory addresses. Use & to get address, * to dereference. Pointers can be used to access and modify values indirectly. Pointer arithmetic allows moving through arrays.