Systems CourseC/C++ · Linux · Memory
C++ Language & SafetyLesson 20 of 441 min read7 questions

Undefined Behavior & Bounds Checking

[] doesn't check; .at() throws; a passing run is not proof.

Test yourself ↓

Concept core idea Watch out easy to get wrong

Concept

Key concepts

  1. arr[i] and array::operator[] do NO bounds checking → out-of-range access is undefined behavior (UB).
  2. UB can look like it works (may print a value, may crash, may corrupt) — 'it printed 4' is NOT proof of correctness.
  3. array::at(i) / vector::at(i) are bounds-checked → throw std::out_of_range.
  4. Catch bugs with sanitizers: -fsanitize=address,undefined.
  5. C++ philosophy: 'don't pay for what you don't use' — speed by default, safety opt-in via .at().

Why [] is unchecked

Checking every index costs a comparison on every access — expensive in tight loops. Other languages (Java/Python) check always (safer, slower, no opt-out). C++ gives both: [] fast, .at() checked.

Watch out

Easily confused

[] vs .at()
[] = fast, no checks, UB on bad index. .at() = bounds-checked, throws std::out_of_range.
UB vs a controlled crash
UB = anything can happen (silent corruption included). .at() throwing (even uncaught → abort) is a controlled, honest failure — far better.
'works' vs correct
A program that runs and prints a value can still be UB. Correctness needs valid indices, not a lucky run.

Test yourself

7 questions on this lesson. Write your answers from memory, then compare them with the model answers side by side.