Pointers: Indirection & nullptr

পয়েন্টার — ঠিকানা ধরে কাজ

Read: ~40 min 20 practice problems

1. Memory Is a Big Array of Bytes

Imagine RAM as a giant numbered array. Every byte has an address. A pointer is just a variable that holds an address.

Memory-কে কল্পনা করুন একটি বিশাল array হিসেবে। প্রতিটি byte-এর একটি unique ঠিকানা আছে। Pointer হলো এমন একটি variable যা সেই ঠিকানা ধরে রাখে।
70x100 420x104 990x108 30x10C int* p = &arr[1]; p ──▶ 0x104 (the int 42) Figure 14.1 — Pointer p holds the address of arr[1].

2. The & and * Operators

ptr.cpp
#include <iostream>

int main() {
    int x = 42;
    int* p = &x;          // p holds the address of x

    std::cout << "x        = " << x  << "\n";
    std::cout << "&x       = " << &x << "\n";  // address
    std::cout << "p        = " << p  << "\n";  // same address
    std::cout << "*p       = " << *p << "\n";  // dereferences → 42

    *p = 100;             // modifies x via pointer
    std::cout << "x now    = " << x  << "\n";
}

3. nullptr — The Null Pointer

A pointer that points to nothing. Always initialize pointers!

null.cpp
#include <iostream>

int main() {
    int* p = nullptr;

    if (p) std::cout << *p;
    else   std::cout << "p is null\n";
}
Dereferencing null is UB *p when p == nullptr = undefined behavior. On most systems it crashes (segmentation fault). Always check before dereferencing.
nullptr vs NULL vs 0 Use nullptr in modern C++. NULL is a macro = 0, which causes overload resolution bugs.

4. Smart Pointer Preview

Modern C++ rarely uses raw pointers for ownership. Instead use std::unique_ptr (sole owner) or std::shared_ptr (shared owner). They free memory automatically when they go out of scope.

smart.cpp
#include <iostream>
#include <memory>

int main() {
    auto p = std::make_unique<int>(42);
    std::cout << *p << "\n";
    // no delete needed — auto-cleanup at scope end
}

Module 17 covers smart pointers in depth.

5. When to Use Raw Pointers

  • Non-owning observation — pointing into a container, optional access.
  • C API interop — when the API requires it.
  • Polymorphic traversal in legacy code.

For ownership: use smart pointers. For "must point to something": use references.

6. Practice Problems

  1. Declare a pointer to an int and have it point to a variable.
    ✨ Show Answer
    int x = 5;
    int* p = &x;
  2. What's sizeof(int*) on a 64-bit system?
    ✨ Show Answer

    8 bytes. All pointers (whatever they point to) have the size of the address bus — 8 bytes on 64-bit, 4 bytes on 32-bit.

  3. What is undefined behavior in int* p; std::cout << *p;?
    ✨ Show Answer

    p is uninitialized → it holds garbage. Dereferencing reads from a random memory location. UB. Always initialize: int* p = nullptr;.

  4. Use a pointer to swap two ints.
    ✨ Show Answer
    void swap(int* a, int* b) {
        int t = *a; *a = *b; *b = t;
    }
    // usage: swap(&x, &y);

    (In modern C++ prefer references — same effect, safer syntax.)

  5. Why prefer nullptr over NULL?
    ✨ Show Answer

    nullptr has its own type (std::nullptr_t). NULL is just 0, an int — it picks the wrong overload when you have f(int) and f(int*).

  6. What does p == nullptr tell you?
    ✨ Show Answer

    The pointer doesn't point to a valid object. Don't dereference it.

  7. Print the address of a variable.
    ✨ Show Answer
    std::cout << &x;
  8. Difference between int* p and int *p?
    ✨ Show Answer

    None — just style. Most C++ guides put the * next to the type: int* p. C convention puts it next to the name: int *p.

  9. What is int** pp?
    ✨ Show Answer

    A pointer to a pointer to int. **pp dereferences twice to reach the int.

  10. Is int* p = &5; legal?
    ✨ Show Answer

    No. 5 is a temporary literal — has no address you can take.

  11. When should you prefer T* over T&?
    ✨ Show Answer

    When you need (a) optional/null, (b) to rebind to point to different objects later, or (c) to interact with a C API.

  12. Show that modifying via pointer changes the original.
    ✨ Show Answer
    int x = 5;
    int* p = &x;
    *p = 10;
    std::cout << x; // 10
  13. Pointer to a vector: std::vector<int>* vp. How to push 5?
    ✨ Show Answer
    vp->push_back(5); // arrow = (*vp).push_back
  14. Why use std::unique_ptr over raw new/delete?
    ✨ Show Answer

    unique_ptr auto-deletes on scope exit (RAII), making leaks impossible. Raw new/delete requires manual management — every new needs exactly one delete, easy to forget on early returns or exceptions.

  15. Make a unique_ptr<int> holding 7.
    ✨ Show Answer
    auto p = std::make_unique<int>(7);
  16. Can two raw pointers point to the same int?
    ✨ Show Answer

    Yes — both can hold the same address. They're independent variables holding the same value. Useful for multiple non-owning observers.

  17. Print the value pointed to and the value of the pointer itself.
    ✨ Show Answer
    std::cout << *p << " " << p; // value, then address
  18. What is a "dangling pointer"?
    ✨ Show Answer

    A pointer to memory that has been freed or to a variable that has gone out of scope. Dereferencing one is UB.

  19. Function with optional pointer param: print value or "none".
    ✨ Show Answer
    void show(int* p) {
        if (p) std::cout << *p;
        else   std::cout << "none";
    }
  20. Modern alternative for "optional value": ?
    ✨ Show Answer

    std::optional<T> (C++17). It's a value type, not a pointer — no nullability bugs, no allocation.

Summary

A pointer holds an address. Use & to take an address, * to dereference. Always initialize pointers (use nullptr). For ownership, use smart pointers; for "must exist", use references; for "may not exist", use std::optional for values or smart pointers for objects.

Next Module → Pointer Arithmetic & Const Correctness.