19-Soft-B17 Data Visualization · December 2014
Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)
Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.
Returning a special sentinel value to signal an error (e.g., a search function returning -1 when an element is not found, or a divide function returning 0 when the divisor is zero) has several well-known weaknesses that exceptions address directly. First, a sentinel value is silently ignorable: nothing forces the caller to check the return value, so a caller who forgets a null/`-1` check simply proceeds with a bad value, and the resulting failure may only surface far away from its true cause (e.g., -1 later used as a valid array index). An uncaught exception, by contrast, immediately unwinds the call stack and halts normal execution (or is deliberately handled), so an error can never be silently swallowed by omission — the program is forced to either handle it or terminate. Second, a sentinel value must be chosen from the function's own return domain, which creates ambiguity when every value in that domain is potentially valid (what special integer could a function that legitimately returns any int, including -1, use to mean "error"?); an exception is a completely separate channel from the normal return value, so this ambiguity cannot arise. Third, exceptions carry rich diagnostic context automatically — a stack trace, an exception type describing exactly what went wrong (e.g., FileNotFoundException vs. PermissionDeniedException), and any custom fields the exception class defines — whereas a sentinel value carries none of that; the caller receiving -1 has no idea whether the cause was a missing file, a permissions problem, or something else. Fourth, exceptions **propagate cleanly through call chains**: a deeply nested function can throw an exception and let it pass, unhandled, through every intermediate caller until a function that actually knows how to handle that kind of error catches it, without every intermediate layer having to manually check and re-propagate a sentinel value.
Exceptions are not free of disadvantages, however. They carry a runtime performance cost in most implementations (stack unwinding and, in some languages, the construction of a stack-trace object), which matters in a tight loop that legitimately expects "not found" as a common, not exceptional, outcome. They can also obscure control flow: a large method wrapped in a broad try/catch, or an exception thrown from deep inside a library, makes it harder to read a piece of code and know every path execution might take, earning exceptions the informal nickname "the goto of modern languages" when overused. Overuse for genuinely normal outcomes is itself an anti-pattern — using an exception to signal "end of loop reached" or "value not present in an optional lookup," where a sentinel or an Optional/Maybe-style type would be clearer and cheaper, mixes up "exceptional" with merely "less common." Finally, checked-exception mechanisms (as in Java) can produce boilerplate catch blocks that developers fill with an empty body just to satisfy the compiler, which silently reintroduces the very "swallowed error" problem exceptions were meant to eliminate — the mechanism only delivers its benefit when handlers are actually written to do something meaningful.