latency.lab
Module 03

Pointers & References

Level: coreTime: ~2 hrsPrereq: Module 02

By the end you can

Pointers

A pointer holds the address of another object. Two operators do the work: & takes an address, * dereferences (reads/writes the pointed-to value).

ptr.cpp
int x = 10;
int* p = &x;        // p holds the address of x
*p = 20;            // write through the pointer; now x == 20
std::cout << *p;    // read through the pointer; prints 20
p = nullptr;        // points to nothing. Dereferencing this is UB.

Always initialize pointers. An uninitialized pointer holds garbage, and dereferencing it is undefined behavior. Use nullptr (never NULL or 0) to mean "points to nothing," and check before dereferencing.

References

A reference is an alias — another name for an existing object. Unlike a pointer, it cannot be null, cannot be reseated to another object, and needs no dereference syntax.

ref.cpp
int x = 10;
int& r = x;      // r is another name for x. Must bind on creation.
r = 20;          // changes x directly; now x == 20

Rule of thumb: use a reference when the thing always exists and you never need to rebind. Use a pointer when it can be null or needs to point at different objects over time. In the order book, an order's location within a price level is naturally a pointer (it can be absent); a function that must operate on an existing book takes a reference.

The three consts

This is a guaranteed interview question. Read const from right to left around the *.

const.cpp
const int* p1;        // pointer to const int: can't change *p1, CAN change p1
int* const p2 = &x;   // const pointer to int: CAN change *p2, can't change p2
const int* const p3 = &x; // const pointer to const int: can't change either
  • const int* p1 — the data is const. You can point p1 elsewhere, but you can't modify what it points to.
  • int* const p2 — the pointer is const. You can modify the pointed-to value, but p2 is stuck pointing where it started.
  • const int* const p3 — both are locked.
The trick

Find the *. Everything left of it that says const applies to the pointee (the data). A const to the right of the * applies to the pointer itself.

Arrays and pointer arithmetic

An array's name decays to a pointer to its first element. Adding 1 to a pointer advances it by one element, not one byte — the compiler scales by sizeof the type.

array.cpp
int arr[4] = {10, 20, 30, 40};
int* p = arr;        // points to arr[0]
std::cout << *(p + 2);  // prints 30 — advanced by 2 ints
std::cout << p[2];      // identical: p[2] means *(p + 2)
Out-of-bounds is UB

arr[4] or arr[-1] reads outside the array. C++ does not bounds-check raw arrays. This is undefined behavior and a top source of security bugs. Prefer std::vector and std::array (Module 07), which can bounds-check with .at().

Dangling pointers

A dangling pointer refers to memory that no longer holds a valid object — because it was freed, or because it went out of scope.

dangle.cpp
int* bad() {
    int local = 42;
    return &local;   // returns address of a stack variable that dies NOW
}                     // caller gets a dangling pointer — UB to use it

Returning the address of a local, keeping a pointer after delete, or holding a pointer into a vector that later reallocates (Module 07) — all produce dangling pointers. Recognizing them on sight is exactly what the debugging round tests.

Build

Exercise 03.1 — Swap and const

Cement the mechanics:

  • Write void swap(int& a, int& b) using references, then a second version void swap(int* a, int* b) using pointers. Note how the reference version reads cleaner at the call site.
  • Write a function size_t count_if_positive(const int* data, size_t n) that walks an array with pointer arithmetic and counts positive values. The const guarantees you won't modify the caller's data.
  • Deliberately write a function that returns &local and compile with -Wall. Read the warning — the compiler catches this one.

Takeaways

  • & takes an address; * dereferences. Always initialize pointers; use nullptr for "none."
  • References are non-null, non-rebindable aliases. Prefer them when the target always exists.
  • The three consts: data-const (const int*), pointer-const (int* const), both. Read around the *.
  • p[i] is *(p + i); pointer math scales by element size. Out-of-bounds is UB.
  • Dangling pointers refer to dead memory. Never return the address of a local.
Live

Loading starter code…

Checkpoint · Pointers & References

6 questions · pass at 70%

Q01
What does const int* p allow and forbid?
Whyconst int* is a pointer to const int: the pointed-to data is read-only, but the pointer itself can be reseated. The const is left of the *, so it binds the data.
Q02
What does int* const p allow and forbid?
Whyint* const is a const pointer to (non-const) int: you may modify the value it points to, but the pointer is locked to one address. The const is right of the *, so it binds the pointer.
Q03
Key difference between a reference and a pointer?
WhyA reference must bind to a valid object at creation and can never be reseated or made null. A pointer can be null and can point to different objects over its lifetime.
Q04
Given int* p = arr;, what is p[3] equivalent to?
Whyp[i] is defined as *(p + i), and pointer arithmetic advances by whole elements (scaled by sizeof(int)), not bytes.
Q05
Why is returning &local from a function a bug?
WhyThe local lives on the stack and is destroyed at function return. Its address then refers to dead memory — using it is undefined behavior.
Q06
What should you always do before dereferencing a pointer that might be null?
WhyDereferencing a null (or garbage) pointer is undefined behavior. Guard with a nullptr check, and initialize pointers to nullptr rather than leaving them uninitialized.

Finished Module 03?

Pass the quiz to complete it automatically.

+ Note