cpp

Pointers Memory

04_Pointers_Memory⚙️
/**
 * ============================================================
 * C++ POINTERS - FUNDAMENTALS
 * ============================================================
 * 
 * This file covers:
 * - What are pointers
 * - Pointer declaration and initialization
 * - Address-of and dereference operators
 * - Pointer arithmetic
 * - Pointers and arrays
 * - Null pointers
 * 
 * Compile: g++ -std=c++17 -Wall 01_pointers.cpp -o pointers
 * Run: ./pointers
 * 
 * ============================================================
 */

#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    cout << "============================================" << endl;
    cout << "     C++ POINTERS - FUNDAMENTALS" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // PART 1: WHAT IS A POINTER?
    // ========================================================
    
    cout << "--- PART 1: WHAT IS A POINTER? ---" << endl << endl;
    
    /*
     * A POINTER is a variable that stores the memory address
     * of another variable.
     * 
     * Memory Visualization:
     * 
     *    Variable    Address      Value
     *    ────────    ─────────    ─────
     *    num         0x1000       42
     *    ptr         0x1008       0x1000 (points to num)
     * 
     * Key Operators:
     * - & (address-of): Gets the address of a variable
     * - * (dereference): Accesses the value at an address
     */
    
    int num = 42;
    int* ptr = &num;  // ptr holds the address of num
    
    cout << "Variable: num" << endl;
    cout << "  Value: " << num << endl;
    cout << "  Address: " << &num << endl;
    
    cout << "\nPointer: ptr" << endl;
    cout << "  Value (address it holds): " << ptr << endl;
    cout << "  Dereferenced (*ptr): " << *ptr << endl;
    cout << "  Address of ptr itself: " << &ptr << endl;
    
    cout << endl;

    // ========================================================
    // PART 2: POINTER DECLARATION
    // ========================================================
    
    cout << "--- PART 2: POINTER DECLARATION ---" << endl << endl;
    
    // Different ways to declare pointers (all equivalent)
    int* ptr1;   // Preferred style
    int *ptr2;   // Also valid
    int * ptr3;  // Also valid
    
    // Declare and initialize
    int value = 100;
    int* pValue = &value;
    
    // Multiple pointers on same line (careful!)
    int *p1, *p2;   // Both are pointers
    int* p3, p4;    // CAREFUL: p3 is pointer, p4 is int!
    
    // Different pointer types
    double d = 3.14;
    double* pDouble = &d;
    
    char c = 'A';
    char* pChar = &c;
    
    cout << "int* pValue = &value;  // " << *pValue << endl;
    cout << "double* pDouble = &d;  // " << *pDouble << endl;
    cout << "char* pChar = &c;      // " << *pChar << endl;
    
    cout << "\n⚠️ Pointer type must match variable type!" << endl;
    cout << "   int* cannot point to double, etc." << endl;
    
    cout << endl;

    // ========================================================
    // PART 3: ADDRESS-OF AND DEREFERENCE
    // ========================================================
    
    cout << "--- PART 3: & AND * OPERATORS ---" << endl << endl;
    
    int x = 10;
    int* px = &x;  // & gets the address of x
    
    cout << "int x = 10;" << endl;
    cout << "int* px = &x;" << endl << endl;
    
    cout << "&x  (address of x): " << &x << endl;
    cout << "px  (pointer value): " << px << endl;
    cout << "*px (dereferenced):  " << *px << endl;
    
    // Modify through pointer
    cout << "\nModifying x through pointer:" << endl;
    cout << "Before: x = " << x << endl;
    *px = 50;  // Change the value at the address px points to
    cout << "After *px = 50: x = " << x << endl;
    
    // Chain of operations
    cout << "\nExpression breakdown:" << endl;
    cout << "x   = " << x << endl;
    cout << "&x  = " << &x << endl;
    cout << "*&x = " << *&x << " (same as x)" << endl;
    cout << "&*px = " << &*px << " (same as px)" << endl;
    
    cout << endl;

    // ========================================================
    // PART 4: POINTER SIZES
    // ========================================================
    
    cout << "--- PART 4: POINTER SIZES ---" << endl << endl;
    
    cout << "Size of pointers on this system:" << endl;
    cout << "sizeof(int*):    " << sizeof(int*) << " bytes" << endl;
    cout << "sizeof(double*): " << sizeof(double*) << " bytes" << endl;
    cout << "sizeof(char*):   " << sizeof(char*) << " bytes" << endl;
    cout << "sizeof(void*):   " << sizeof(void*) << " bytes" << endl;
    
    cout << "\n💡 All pointer types have the same size" << endl;
    cout << "   (typically 8 bytes on 64-bit systems)" << endl;
    
    cout << endl;

    // ========================================================
    // PART 5: NULL POINTERS
    // ========================================================
    
    cout << "--- PART 5: NULL POINTERS ---" << endl << endl;
    
    /*
     * A null pointer points to nothing (address 0)
     * Used to indicate "no valid address"
     * 
     * C++11: Use nullptr (preferred)
     * Old C/C++: Use NULL or 0
     */
    
    int* nullPtr = nullptr;  // Modern C++ way
    int* nullPtr2 = NULL;    // Old way (still works)
    int* nullPtr3 = 0;       // Also works, not recommended
    
    cout << "nullptr value: " << nullPtr << endl;
    
    // Always check before dereferencing!
    if (nullPtr != nullptr) {
        cout << "Safe to dereference" << endl;
    } else {
        cout << "Pointer is null - do not dereference!" << endl;
    }
    
    // Shorthand null check
    if (nullPtr) {  // Same as: if (nullPtr != nullptr)
        cout << "Pointer is valid" << endl;
    } else {
        cout << "Pointer is null" << endl;
    }
    
    // ⚠️ Dereferencing nullptr causes UNDEFINED BEHAVIOR!
    // *nullPtr;  // CRASH! Never do this!
    
    cout << endl;

    // ========================================================
    // PART 6: POINTER ARITHMETIC
    // ========================================================
    
    cout << "--- PART 6: POINTER ARITHMETIC ---" << endl << endl;
    
    /*
     * Pointer arithmetic moves by the SIZE of the pointed type
     * 
     * ptr + 1: moves forward by sizeof(*ptr) bytes
     * ptr - 1: moves backward by sizeof(*ptr) bytes
     */
    
    int arr[] = {10, 20, 30, 40, 50};
    int* p = arr;  // Points to first element
    
    cout << "Array: {10, 20, 30, 40, 50}" << endl << endl;
    
    cout << "int* p = arr;" << endl;
    cout << "*p     = " << *p << "  (element 0)" << endl;
    cout << "*(p+1) = " << *(p+1) << "  (element 1)" << endl;
    cout << "*(p+2) = " << *(p+2) << "  (element 2)" << endl;
    cout << "*(p+3) = " << *(p+3) << "  (element 3)" << endl;
    cout << "*(p+4) = " << *(p+4) << "  (element 4)" << endl;
    
    // Show actual addresses
    cout << "\nAddresses (showing pointer arithmetic):" << endl;
    cout << "p+0 = " << (p+0) << endl;
    cout << "p+1 = " << (p+1) << " (+" << sizeof(int) << " bytes)" << endl;
    cout << "p+2 = " << (p+2) << " (+" << 2*sizeof(int) << " bytes)" << endl;
    
    // Increment and decrement
    cout << "\nIncrement/Decrement:" << endl;
    p = arr;
    cout << "*p = " << *p << endl;
    p++;  // Move to next element
    cout << "After p++: *p = " << *p << endl;
    p += 2;  // Move forward by 2
    cout << "After p+=2: *p = " << *p << endl;
    p--;  // Move back by 1
    cout << "After p--: *p = " << *p << endl;
    
    cout << endl;

    // ========================================================
    // PART 7: POINTERS AND ARRAYS
    // ========================================================
    
    cout << "--- PART 7: POINTERS AND ARRAYS ---" << endl << endl;
    
    int numbers[] = {1, 2, 3, 4, 5};
    int* pArr = numbers;  // Array name is a pointer to first element
    
    cout << "Array name acts like a pointer:" << endl;
    cout << "numbers    = " << numbers << endl;
    cout << "&numbers[0] = " << &numbers[0] << " (same!)" << endl;
    cout << "pArr       = " << pArr << endl;
    
    cout << "\nTwo ways to access elements:" << endl;
    for (int i = 0; i < 5; i++) {
        cout << "numbers[" << i << "] = " << numbers[i] 
             << "   *(pArr+" << i << ") = " << *(pArr+i) << endl;
    }
    
    // Array notation vs pointer notation
    cout << "\nEquivalent expressions:" << endl;
    cout << "arr[i]   ←→  *(arr + i)" << endl;
    cout << "&arr[i]  ←→  arr + i" << endl;
    
    cout << endl;

    // ========================================================
    // PART 8: POINTER TO POINTER
    // ========================================================
    
    cout << "--- PART 8: POINTER TO POINTER ---" << endl << endl;
    
    int val = 100;
    int* pVal = &val;
    int** ppVal = &pVal;  // Pointer to pointer
    
    cout << "int val = 100;" << endl;
    cout << "int* pVal = &val;" << endl;
    cout << "int** ppVal = &pVal;" << endl << endl;
    
    cout << "val      = " << val << endl;
    cout << "*pVal    = " << *pVal << " (value through pointer)" << endl;
    cout << "**ppVal  = " << **ppVal << " (value through pointer to pointer)" << endl;
    
    cout << "\nAddresses:" << endl;
    cout << "&val     = " << &val << endl;
    cout << "pVal     = " << pVal << " (holds &val)" << endl;
    cout << "&pVal    = " << &pVal << endl;
    cout << "ppVal    = " << ppVal << " (holds &pVal)" << endl;
    cout << "*ppVal   = " << *ppVal << " (holds &val)" << endl;
    
    cout << endl;

    // ========================================================
    // PART 9: CONST AND POINTERS
    // ========================================================
    
    cout << "--- PART 9: CONST AND POINTERS ---" << endl << endl;
    
    int a = 10, b = 20;
    
    // Pointer to const (can't modify value through pointer)
    const int* pc = &a;
    // *pc = 100;  // ERROR: can't modify
    pc = &b;       // OK: can change what it points to
    
    // Const pointer (can't change what it points to)
    int* const cp = &a;
    *cp = 100;     // OK: can modify value
    // cp = &b;    // ERROR: can't change pointer
    
    // Const pointer to const (can't do either)
    const int* const cpc = &a;
    // *cpc = 100;  // ERROR
    // cpc = &b;    // ERROR
    
    cout << "const int* pc:        Can't modify *pc, can reassign pc" << endl;
    cout << "int* const cp:        Can modify *cp, can't reassign cp" << endl;
    cout << "const int* const cpc: Can't do either" << endl;
    
    cout << "\n💡 Read right to left:" << endl;
    cout << "const int* p → p is a pointer to const int" << endl;
    cout << "int* const p → p is a const pointer to int" << endl;
    
    cout << endl;

    cout << "============================================" << endl;
    cout << "POINTERS SUMMARY:" << endl;
    cout << "============================================" << endl;
    cout << "• Pointer stores memory address" << endl;
    cout << "• & gets address, * dereferences" << endl;
    cout << "• Use nullptr for null pointers" << endl;
    cout << "• Always check for nullptr before use" << endl;
    cout << "• Arrays decay to pointers" << endl;
    cout << "• const placement matters!" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Write a function that swaps two integers using pointers
 * 
 * 2. Write a function that reverses an array using pointers only
 *    (no array indexing [])
 * 
 * 3. Write a function that finds the maximum element in an array
 *    using pointer arithmetic
 * 
 * 4. Create a pointer to a 2D array and traverse it
 * 
 * 5. Write a function that returns a pointer to the largest
 *    element in an array
 */
Pointers Memory - C++ Tutorial | DeepML