Answer these before opening the key. Several questions deliberately distinguish undefined behavior from defined-but-dangerous behavior.
- Why can a non-null pointer still be invalid?
- What is the difference between default-initializing
new Pointand value-initializingnew Point{}whenPointhas no user-provided constructor? - May a one-past pointer be formed, and may it be dereferenced?
- Why does passing an array as
T*make bounds checking harder? - Why is checking for signed overflow after evaluating
a + bunreliable? - For
int index = -1, why isindex < vector.size()normally false rather than true? - Is converting
300to an 8-bitunsigned charundefined behavior? - Which vector event invalidates all iterators, references, and pointers to its elements?
- Does
eraseinvalidate the same positions for every standard container? - What does
std::string_viewown? - Name three events that can dangle a
string_view. - Why can
[&local]and[this]both create lifetime bugs in deferred callbacks? - Is object slicing undefined behavior? What information is lost?
- Why must
new[]pair withdelete[], and placementnewpair with explicit destruction? - How does a shallow copy of a raw owning pointer lead to double deletion?
- After moving from
std::string, may the source be destroyed or assigned to? May its old text be assumed? - What state does a successfully moved-from
std::unique_ptrhave? - Why is “check under a lock, unlock, then use” not one atomic invariant?
- Why can a filesystem existence check followed by open be a TOCTOU bug?
- Why can ordinary debugger breakpoints hide a data race?
- Does
memory_order_relaxedpublish unrelated payload data? - Why does virtual dispatch select
BaseinsideBase::Base()? - What is missing from
address + sizeof(T)before the storage can be accessed as a liveT? - Is matching
sizeofenough to prove ABI compatibility? - If a class declares a destructor but no copy operations, what does the compiler still generate, and why is that dangerous for a raw owning pointer?
- Two objects hold
shared_ptrmembers pointing at each other. What are their use counts at scope exit, and which destructor runs? - Does reading
map["missing"]change the container? - Why can you not bind
bool&to an element ofstd::vector<bool>? - What does
auto x = f();deduce whenfreturnsT&, and what is the runtime cost? - A derived class declares
void log(double). Why doesderived.log("text")fail to compile even though the base has a string overload? - A virtual function is overridden with a different default argument. Which default applies when called through a base reference?
- What does
Timer t();declare inside a function body? - In which order are members initialized, and what determines it?
- Why is
0.1 + 0.2 == 0.3false, and why is a fixed absolute epsilon insufficient? container.size() - 1on an empty container yields what, and is it undefined behavior?- What happens when a joinable
std::threadreaches its destructor?
- Run
Trap05_SignedOverflow_unsafe, break in each of its three functions, and classify each result as UB or defined behavior. Explain why sanitizer output is not expected for all three. - In
Trap07_IteratorInvalidation, record vectordata()andcapacity()before and after growth. Reacquire the iterator and explain why the numeric address is evidence, not the language rule itself. - In
Trap08_StringView, draw two boxes: the view and its owner. Step through each variant and mark the exact event that breaks the relationship. - In
Trap12_ObjectSlicing, compare the dynamic type before and after a by-value copy. Confirm that no memory error is required for polymorphism to disappear. - In
Trap19_DoubleFree_unsafe, use AddressSanitizer and identify the first ownership mistake, the first release, and the later reported failure. They may be different locations. - In
Trap21_CheckThenAct, write the smallest possible competing-thread/process timeline between the check and use. Do not rely on single-step execution to reproduce it. - In
Trap29_RawBytes, stop beforeconstruct_at, after construction, and afterdestroy_at. At each stop, state whether storage exists and whether aRecordobject is alive. - In
Trap31_ShallowCopy_unsafe, run under AddressSanitizer and identify which of the two destructors reports the failure. Then explain why the real defect is the missing copy constructor, not the second destructor. - In
Trap32_SharedPtrCycle, loguse_count()at three points and explain why the cyclic case never reaches zero. Confirm that changing one edge toweak_ptrrestores both destructor calls. - In
Trap35_AutoDropsRef, print the address of the source object and of each deduced variable. Classify each ofauto,auto&,const auto&, anddecltype(auto)as copy or alias. - In
Trap39_MemberInitOrder_unsafe, break in the constructor and record the value ofcountat the momentdoubledis computed. Explain why reordering the init-list would not fix it. - In
Trap41_UnsignedUnderflow, evaluatev.size() - 1for an empty container in the watch window. Explain why this is defined behavior yet still a bug, and why no sanitizer reports it.
- Pointer value alone proves neither lifetime, bounds, alignment, permitted type access/provenance, nor synchronization.
new Pointdefault-initializes; scalar members may remain indeterminate.new Point{}value-initializes and zero-initializes those members in this case.- It may be formed for comparison/arithmetic within the allowed domain, but not dereferenced.
- Array-to-pointer decay discards the extent; pass a range/container,
std::span, or an explicit validated count. - The overflowing signed operation is already UB, so the optimizer may assume it never happened. Check before evaluating it or use checked arithmetic.
- The usual arithmetic conversions convert
-1to a large unsignedsize_t; that value is not less than the small size. The result is defined but often contrary to the intended range test. - No. Conversion to an unsigned destination is defined modulo one more than its maximum value. It can still be unwanted information loss.
- Reallocation to a new backing allocation.
- No. Invalidation rules are container- and operation-specific; learn and check the relevant contract.
- Nothing. It is a borrowed pointer-and-length view.
- Owner destruction/scope exit, destruction of a temporary owner, and owner mutation that reallocates or otherwise invalidates references.
- The closure stores a reference or object pointer, not an owned snapshot; invocation can occur after the referent/object lifetime ends.
- No. Copying into a
Baseobject is defined; the derived subobject/state and derived dynamic type are not part of the copied destination. - The allocation/deallocation and lifetime protocols must match. A mismatch violates the required contract; placement construction does not arrange automatic destruction.
- Both copies believe they own the same allocation, so their cleanup paths release the same resource twice. Use value semantics or a single explicit RAII owner.
- Yes, it remains valid and can be destroyed or assigned to; its particular value is generally unspecified, so the old text cannot be assumed.
- It is empty/null after a successful move.
- Another actor can change or destroy the checked state after unlock and before use. Protect the complete check-and-use sequence or acquire an owning snapshot.
- Another process can replace or change the path between operations. Prefer an atomic/open-and-validate API and operate on the acquired handle.
- Pausing threads changes their interleaving and may accidentally serialize the conflicting accesses. Use ThreadSanitizer or deliberate schedule instrumentation.
- No. It makes that atomic access atomic but does not by itself publish unrelated payload; use a release/acquire protocol or mutex.
- During base construction the derived subobject is not yet active, so virtual dispatch is limited to the currently constructed class.
- Suitable alignment, storage duration, a begun
Tlifetime, valid provenance, permitted typed access, and satisfied object invariants. - No. Alignment, offsets, packing, calling convention, runtime/allocator, ownership, exception rules, and representation must also agree.
- The copy constructor and copy assignment are still generated and copy the pointer value, so two objects claim one allocation and both release it. Declare copy and move operations, or hold the resource in a member that owns it correctly.
- Both counts remain at 1 after the scope-local owners are destroyed, so neither destructor runs and both objects leak. Exactly one edge of the cycle must be a
weak_ptr. - Yes.
operator[]default-constructs and inserts a value for a missing key. Usefind,at, orcontainswhen the intent is to read. - It is a bit-packed specialization with no addressable
boolelements.operator[]returns a proxy object that writes through to the packed bits. - It deduces
T, stripping the reference and top-level const, so a full copy is made and later writes do not reach the original. - Declaring any member named
loghides every base overload of that name. Addusing Base::log;to bring them back into the overload set. - The base default applies. The body is selected by the dynamic type, but default arguments are substituted from the static type; keep defaults out of virtual functions.
- A function named
ttaking no parameters and returningTimer. UseTimer t;orTimer t{};to define an object. - In the order the members are declared in the class, after all base subobjects. The order written in the member-initializer list is irrelevant, so a member must never be initialized from a member declared after it.
- Neither operand is exactly representable in binary floating point, so the sum differs from the literal
0.3by a rounding error. A fixed epsilon fails for large magnitudes, where the smallest representable gap already exceeds it; scale the tolerance to the operands. - It yields the maximum value of the unsigned type. This is defined modular arithmetic, not undefined behavior, which is exactly why no sanitizer flags it. Guard with
empty()or use signed sizes. - Its destructor calls
std::terminateand the process aborts. Join or detach on every path, or usestd::jthread, which joins in its destructor.
- Exercise 1: signed overflow is UB; mixed-sign comparison and unsigned narrowing are defined, though potentially buggy.
- Exercise 2: a changed
data()demonstrates that relocation occurred; the standard invalidation rule is what makes old handles invalid. - Exercise 3: the view never extends owner lifetime, and reallocation can invalidate it before owner destruction.
- Exercise 4: slicing is a semantic/type-model bug, so ASan silence is expected.
- Exercise 5: repair ownership at its origin, not merely the second
deletesite. - Exercise 6: the bug is the unprotected interval; a deterministic crash is not required.
- Exercise 7: raw storage exists at all three stops, but a live
Recordexists only between construction and destruction. - Exercise 8: ASan reports the second release, while the defect originates in the implicitly generated copy.
- Exercise 9: the cyclic pair holds each other alive at count 1; the
weak_ptredge does not contribute to ownership. - Exercise 10: only
autocopies; the other three alias the original object. - Exercise 11:
countis still indeterminate, because declaration order fixed the sequence before the init-list was written. - Exercise 12: the wrap is defined behavior, so the bug is visible only through the invariant, not through a tool.