Skip to main content

Memory & Language Features

Pointers

Complete lesson for Pointers using LEARNCODEGURU’s 6-level learning model — from basics to advanced practice, with C++ OOP and STL where relevant.

L1

Basics — Pointers

Section: Memory & Language Features. C++ pointers — addresses, dynamic allocation, new/delete, and smart pointers (unique_ptr, shared_ptr, weak_ptr).

What is a pointer?

  • A **pointer** is a variable that stores a **memory address** of another variable (or heap block).
  • Declare: int* p; — “pointer to int”. Initialize with address-of: p = &x;
  • **Dereference** *p reads/writes the object at that address.
  • **Real-life analogy:** a pointer is like a house address on a slip of paper; *p is visiting the house.
  • **Real-life analogy:** a remote control (pointer) selects a TV (object); the remote is not the TV.
ConceptSyntaxMeaning
Declare pointerint* p;p will hold an address of an int
Address-ofp = &x;Store address of x in p
Dereference*p = 20;Change the int at that address
Null pointerp = nullptr;Points to nothing (safe empty)
Print addresscout << p;Shows the stored address
#include <iostream>
using namespace std;

int main() {
    int x = 10;
    int* p = &x;
    cout << "x = " << x << "\n";
    cout << "&x = " << &x << "\n";
    cout << "p = " << p << "\n";
    cout << "*p = " << *p << "\n";
    *p = 25;
    cout << "x now = " << x << "\n";
    return 0;
}

nullptr and pointer safety

  • Use **nullptr** (C++11) for an empty pointer — prefer it over NULL or 0.
  • Never dereference a null or uninitialized pointer — undefined behaviour / crash.
  • After delete, set the pointer to nullptr so you do not keep a **dangling** address.
  • Uninitialized pointers hold garbage addresses — always initialize (nullptr or &var or new).
int* p = nullptr;
if (p != nullptr) {
    cout << *p; // only if valid
}

Pointers and arrays

  • An array name can decay to a pointer to its first element: int* p = a;
  • p[i] is like *(p + i).
  • Pointer arithmetic moves in units of the pointed type (p+1 → next int, not next byte).
int a[5] = {10, 20, 30, 40, 50};
int* p = a;
cout << *p << "\n";     // 10
cout << *(p + 2) << "\n"; // 30
cout << p[3] << "\n";    // 40

Static vs dynamic allocation (differences)

  • **Static / automatic allocation:** size known mainly at compile time; memory managed for you (globals/statics duration or stack locals).
  • **Dynamic allocation:** size decided at **runtime**; you allocate on the **heap** with new / new[] and free with delete / delete[].
  • Pointers are how you hold and use dynamically allocated memory.
PointStatic / automaticDynamic (heap)
When size is knownMostly at compile timeAt runtime (user input, varying needs)
Where memory livesData segment / **stack** (locals)**Heap**
How you get itint x;, int a[10];new int, new int[n]
Who frees itAutomatic (end of scope / program)You (delete / delete[]) or smart pointers
LifetimeFixed by scope/storage durationUntil you free it (or process ends)
FlexibilityLimited for unknown sizesHigh — grow/allocate as needed
RiskStack overflow if huge localsLeaks, dangling pointers if mismanaged
Typical useSmall locals, fixed arraysLarge/variable arrays, linked structures
AccessVariable name directlyThrough a pointer
Exampleint marks[50];int* marks = new int[n];

Dynamic memory allocation in C++

  • C++ uses **new** / **new[]** to allocate and **delete** / **delete[]** to free (prefer these over C’s malloc/free in C++ code).
  • Allocated memory stays until freed — forgetting delete causes a **memory leak**.
  • Match pairs: new ↔ delete, new[] ↔ delete[].
  • **Real-life:** renting a hall for a wedding (allocate), use it, then return the keys (delete) — if you never return it, you keep paying (leak).
OperatorPurposeExample
newAllocate one objectint* p = new int;
new T(value)Allocate and construct/initint* p = new int(10);
new[]Allocate an arrayint* a = new int[5];
deleteFree one objectdelete p;
delete[]Free an arraydelete[] a;

Dynamic memory — new, delete, heap & smart pointers

Dynamic memory is allocated at runtime from the heap using new / new[] and freed with delete / delete[]. The diagram covers why it is used, stack vs heap, array/2D examples, leaks, dangling pointers, and smart pointers.

C++ dynamic memory infographic: heap allocation, new and delete operators, stack vs heap diagram, single variable and array examples, 2D array of pointers, memory leaks, dangling pointers, and unique_ptr smart pointer.
C++ dynamic memory — allocate with new, use it, free with delete (or smart pointers)
  • new / new[]: allocate one object or an array on the heap.
  • delete / delete[]: free matching allocations — never mismatch.
  • Leak: forgot delete · Dangling: use after delete.
  • Prefer: unique_ptr / vector for safer ownership.

Dynamic allocation — single object

  • int* p = new int; allocates one int on the heap; *p uses it; delete p; frees it.
  • Initialize: int* p = new int(42);
  • After delete: p = nullptr;
#include <iostream>
using namespace std;

int main() {
    int* p = new int(10);
    cout << *p << "\n";
    *p = 25;
    cout << *p << "\n";
    delete p;
    p = nullptr;
    return 0;
}

Dynamic allocation — arrays

  • Size can come from the user: int n; cin >> n; int* a = new int[n];
  • Index with a[i] just like a normal array.
  • Free with **delete[] a;** (not delete a).
  • **Real-life:** create a marks list for N students only after N is entered.
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "How many marks? ";
    cin >> n;
    int* marks = new int[n];
    for (int i = 0; i < n; i++) {
        cin >> marks[i];
    }
    int sum = 0;
    for (int i = 0; i < n; i++) sum += marks[i];
    cout << "Average = " << (sum / (double)n) << "\n";
    delete[] marks;
    marks = nullptr;
    return 0;
}

Dynamic 2D arrays (array of pointers)

  • One common pattern: allocate an array of row pointers, then each row with new int[cols].
  • Free in reverse: delete[] each row, then delete[] the row-pointer array.
  • **Real-life:** cinema seats rows×cols when dimensions are chosen at runtime.
#include <iostream>
using namespace std;

int main() {
    int rows = 2, cols = 3;
    int** mat = new int*[rows];
    for (int r = 0; r < rows; r++)
        mat[r] = new int[cols];

    int k = 1;
    for (int r = 0; r < rows; r++)
        for (int c = 0; c < cols; c++)
            mat[r][c] = k++;

    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) cout << mat[r][c] << " ";
        cout << "\n";
    }

    for (int r = 0; r < rows; r++) delete[] mat[r];
    delete[] mat;
    return 0;
}

Stack vs heap (where pointers fit)

  • Local variables and the pointer variable itself often live on the **stack**.
  • new memory lives on the **heap**; the pointer on the stack stores that heap address.
  • When a function returns, stack locals vanish — but heap blocks remain until delete (or process exit).
RegionHoldsFreed how
StackLocals, parameters, pointer variablesAutomatic at end of scope
HeapObjects from new / new[]delete / delete[] / smart pointer

Pointers vs references (quick)

  • Pointers can be reseated and can be nullptr; references must bind and usually cannot be null.
  • Prefer references for APIs when “must refer to an object”; use pointers for optional/ reseatable / dynamic ownership.
PointPointerReference
StoresAddress (can change)Alias (bound at init)
NullYes (nullptr)No (normally)
Syntax*p, p->r directly
ReseatYes p = &y;No
Dynamic allocYes — hold new resultNot for owning new memory
L2

Advanced — Pointers

Memory leaks and dangling pointers

  • **Leak:** allocate with new and never delete — memory stays reserved until the program ends.
  • **Dangling:** pointer still holds an address after delete — using it is undefined behaviour.
  • **Double delete:** deleting the same block twice is also undefined behaviour.
  • Fix dangling: delete p; p = nullptr; and never use after free.
int* p = new int(5);
delete p;      // OK
// *p = 1;     // BAD — dangling
p = nullptr;   // safer
// delete p;   // OK on nullptr (no-op)

new vs malloc (C++)

  • In C++ prefer new/delete: they call constructors/destructors for class types.
  • malloc/free are C-style; do not mix new with free or malloc with delete.
  • new can throw std::bad_alloc on failure (unless nothrow form).
Featurenew / deletemalloc / free
LanguageC++C (also usable in C++)
ConstructorsYesNo
Type safetyTyped resultvoid* (cast needed in C++)
FailureThrows / nullptr (nothrow)Returns null
PairingMatch new/delete formsMatch malloc/free

Smart pointers (modern C++)

  • **Smart pointers** (#include ) own heap memory and free it automatically — RAII.
  • Prefer them over raw new/delete in application code to avoid leaks and dangling pointers.
  • Main types: unique_ptr, shared_ptr, weak_ptr. Prefer make_unique / make_shared to create them.
  • **Real-life:** a smart locker that returns itself when you leave — you do not manually “delete” the room.
Smart pointerOwnershipCopy?When to use
unique_ptrExclusive (one owner)No (movable only)Default choice for single ownership
shared_ptrShared (ref-counted)YesSeveral owners need the same object
weak_ptrNon-owning observerYes (weak)Break cycles; cache/observe without keeping alive

unique_ptr — exclusive ownership

  • Only one unique_ptr owns the object at a time.
  • When the unique_ptr is destroyed (end of scope), it calls delete automatically.
  • Move with std::move to transfer ownership; cannot copy.
  • Array form: unique_ptr or make_unique(n).
#include <iostream>
#include <memory>
using namespace std;

int main() {
    unique_ptr<int> p = make_unique<int>(42);
    cout << *p << "\n";

    unique_ptr<int> q = std::move(p); // ownership moves to q
    // p is now empty (nullptr)
    if (!p) cout << "p is empty\n";
    cout << *q << "\n";
    // automatic delete when q goes out of scope
    return 0;
}

shared_ptr and weak_ptr

  • shared_ptr keeps a **reference count** — object freed when the last owner is gone.
  • Copying a shared_ptr increases the count; destroying one decreases it.
  • weak_ptr observes a shared_ptr **without** increasing the count — use lock() to get a temporary shared_ptr.
  • Use weak_ptr to avoid **cycles** (A owns B and B owns A via shared_ptr → never freed).
#include <iostream>
#include <memory>
using namespace std;

int main() {
    shared_ptr<string> a = make_shared<string>("Hello");
    shared_ptr<string> b = a; // share ownership
    cout << *a << " use_count=" << a.use_count() << "\n"; // 2

    weak_ptr<string> w = a;
    if (auto s = w.lock()) {
        cout << "Still alive: " << *s << "\n";
    }
    return 0;
}

Raw pointer vs smart pointer

  • Use raw pointers for non-owning observation; use smart pointers when you own heap memory.
PointRaw T*Smart pointer
Owns memory?Only if you say so (manual)Yes (by design)
Auto free?No — you deleteYes at end of ownership
Null?nullptr OKCan be empty
Best forOptional / borrow / arrays APIsOwnership of new objects
Leak riskHigh if you forget deleteLow when used correctly

Pointer to pointer and functions

  • int** is a pointer to a pointer — used for 2D dynamic arrays and out-parameters that reseat a pointer.
  • Passing int* lets a function modify the pointed-to object; passing int*& or int** can reseat the caller’s pointer.
void setToHeap(int*& p, int value) {
    delete p;
    p = new int(value);
}

Real-life models

  • Hotel room booking: allocate a room (new), use it, checkout (delete) — or use unique_ptr so checkout is automatic.
  • Playlist of unknown length: dynamic array/vector of song IDs.
  • Linked list / tree nodes: each node allocated dynamically; prefer unique_ptr/shared_ptr for ownership.
  • Optional sensor: raw/observer pointer is nullptr when device absent; non-null when connected.
  • Document shared by many tabs: shared_ptr-like shared ownership until the last tab closes.
L3

Tricky Interview Points

Q1) What is a pointer in C++?

Answer A variable that stores the memory address of another object. You create it with types like int*, set it with &x, and access the object with *p.

Q2) What is the difference between static/automatic and dynamic allocation?

Answer Static/automatic allocation has size known mainly at compile time and is managed automatically (stack/globals). Dynamic allocation uses the heap via new/new[], size can be runtime, and you must free with delete/delete[] (or smart pointers).

Q3) Where does new allocate memory?

Answer On the heap. The pointer variable that holds the address often lives on the stack.

Q4) Difference between new and new[]? delete and delete[]?

Answer new allocates one object; new[] allocates an array. Free with delete and delete[] respectively — never mismatch them.

Q5) What is a memory leak?

Answer Allocating heap memory and never freeing it, so it remains reserved until the program ends.

Q6) What is a dangling pointer?

Answer A pointer that still holds the address of memory that was already freed (or of a destroyed object). Using it is undefined behaviour.

Q7) Why use nullptr?

Answer It is the type-safe null pointer literal in C++11+. Prefer it over NULL or 0 for empty pointers.

Q8) What is pointer arithmetic?

Answer Adding/subtracting an integer moves the pointer by that many elements of the pointed type, e.g. p+1 points to the next int.

Q9) Can you allocate an array whose size is read from the user?

Answer Yes — dynamically: int n; cin>>n; int* a = new int[n]; … delete[] a;

Q10) new vs malloc in C++?

Answer Prefer new/delete because they are typed and invoke constructors/destructors. Do not mix new with free or malloc with delete.

Q11) What are smart pointers?

Answer RAII wrappers from <memory> (unique_ptr, shared_ptr, weak_ptr) that free owned heap memory automatically when ownership ends.

Q12) unique_ptr vs shared_ptr?

Answer unique_ptr has exclusive ownership and cannot be copied (only moved). shared_ptr shares ownership with reference counting and can be copied.

Q13) What is weak_ptr for?

Answer A non-owning observer of an object managed by shared_ptr. It does not keep the object alive and helps break ownership cycles. Use lock() to access safely.

Q14) Why prefer make_unique / make_shared?

Answer They are exception-safer and clearer than writing new yourself, and make_shared can be more efficient by combining control-block and object allocation.

Q15) Pointer vs reference?

Answer Pointers store addresses, can be null/reseated, use * and ->. References are aliases, must bind, usually non-null, and use direct syntax.

L4

Simple Programs

Pointer basics — modify via *p

#include <iostream>
using namespace std;

int main() {
    int x = 5;
    int* p = &x;
    *p = *p + 10;
    cout << x << "\n"; // 15
    return 0;
}

Dynamic single int

#include <iostream>
using namespace std;

int main() {
    int* p = new int(100);
    cout << *p << "\n";
    delete p;
    p = nullptr;
    return 0;
}

Dynamic array — sum N numbers

#include <iostream>
using namespace std;

int main() {
    int n = 5;
    int* a = new int[n]{1, 2, 3, 4, 5};
    int sum = 0;
    for (int i = 0; i < n; i++) sum += a[i];
    cout << sum << "\n";
    delete[] a;
    return 0;
}

Static array vs dynamic array

#include <iostream>
using namespace std;

int main() {
    int fixed[3] = {10, 20, 30}; // automatic/static-size
    int n = 3;
    int* dyn = new int[n]{10, 20, 30}; // dynamic
    cout << fixed[1] << " " << dyn[1] << "\n";
    delete[] dyn;
    return 0;
}

Swap using pointers

#include <iostream>
using namespace std;

void swap(int* a, int* b) {
    int t = *a; *a = *b; *b = t;
}

int main() {
    int x = 3, y = 7;
    swap(&x, &y);
    cout << x << " " << y << "\n";
    return 0;
}

unique_ptr demo

#include <iostream>
#include <memory>
using namespace std;

int main() {
    auto p = make_unique<int[]>(5);
    for (int i = 0; i < 5; i++) p[i] = (i + 1) * 10;
    cout << p[2] << "\n"; // 30
    return 0;
}

shared_ptr — shared ownership

#include <iostream>
#include <memory>
#include <string>
using namespace std;

int main() {
    auto a = make_shared<string>("C++");
    auto b = a;
    cout << *b << " owners=" << a.use_count() << "\n";
    return 0;
}

Leak vs correct free (demo comments)

#include <iostream>
using namespace std;

int main() {
    int* leak = new int(1);
    // forgot delete — leak (don't do this)

    int* ok = new int(2);
    cout << *ok << "\n";
    delete ok;
    ok = nullptr;
    delete leak; // free for demo completeness
    return 0;
}
L5

Problem Solving Tricks

  • Always initialize pointers (nullptr, &x, or new).
  • Never dereference null or dangling pointers.
  • Match new with delete and new[] with delete[].
  • After delete, set the pointer to nullptr.
  • Need runtime size? → dynamic allocation (or std::vector).
  • Prefer std::vector / smart pointers over raw new[] in application code.
  • Default owning pointer: unique_ptr; share only when needed: shared_ptr.
  • Use weak_ptr to observe shared_ptr without keeping the object alive.
  • Prefer make_unique / make_shared over naked new.
  • Pointer arithmetic steps by element size, not bytes.
  • Stack = automatic locals; heap = new memory.
  • Do not mix malloc/free with new/delete.
  • Draw boxes for stack pointer and heap block when debugging.
  • const int* vs int* const — pointer to const vs const pointer.
  • For ownership, think RAII: unique_ptr first.
L6

Practice Programs List

  1. Print address and value of an int using a pointer.
  2. Change a variable’s value only through a pointer.
  3. Write a table (on paper) comparing static vs dynamic allocation with 5 points.
  4. Allocate one double with new, read/write it, delete it.
  5. Read n from user, allocate int[n], input values, print max, delete[].
  6. Create a dynamic 2D matrix rows×cols, fill, print, free correctly.
  7. Demonstrate a dangling pointer bug, then fix it with nullptr.
  8. Implement swap with pointers and with references; compare.
  9. Rewrite a dynamic array program using unique_ptr or vector.
  10. Show pointer arithmetic on an array of 5 ints.
  11. Write a function that takes int* and doubles each of n elements.
  12. Explain in comments stack vs heap for: int x; int* p = new int;
  13. Intentionally mismatch new[] with delete (in a throwaway build) and note why it is wrong — then fix it.
  14. List three real-life cases where dynamic allocation is needed.