Concepts / Range Checking in Vector Operations

Range Checking in Vector Operations

The word range-checked describes the behavior you would expect: the vector verifies that an index falls within the valid range of positions before accessing an element.

  • Programming
Interactive lab

Try it: try / except / else / finally

How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.

How it works

  1. Statements in try run until one raises an exception; the rest of try is skipped.
  2. Python checks the except clauses in order and runs the first one whose type matches.
  3. If nothing was raised, the else block runs instead.
  4. finally runs in every case.

Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.

Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.

Educational simulation

Loading the simulation…

When Nothing Catches the Error

Suppose a vector operation fails in the middle of a long program. Somewhere in the code, an exception has been thrown to signal that something went wrong, but no catch clause anywhere in the program is written to receive it. What should the program do with that error? Should the failure be silent, should it be printed somewhere, or should something else happen? Decide on a sensible default before reading on, because that is exactly the question the default exception handler answers.

The source describes the rule in one sentence: an exception that no handler catches reaches a default exception handler. This article follows one thrown exception from the point where it is raised to the point where the default handler reports it, and explains why that default matters even when you have written careful handling code elsewhere.

How the Runtime Searches for a Handler

When code throws an exception, control does not simply continue to the next line. The runtime looks for a handler that matches the kind of exception that was thrown. If a matching catch clause exists, that handler takes over and the program can deal with the problem in its own way. If no matching handler exists, the exception keeps going unhandled, and the default behavior provided for this situation takes effect.

Key point

A throw is a request for a handler, not a command to print something. Whether anything is printed depends on whether a matching handler exists somewhere along the path the exception takes.

exception thrownmatching handlerfound?handler takes overdefault handlerreports on cerrprogram terminatessearchyesno
What is the sequence of steps from the moment an exception is thrown to program termination, and where does the default handler fit into that sequence?

Unwinding Through Nested Calls

An exception may be thrown deep inside nested function calls, and the code that threw it may have no idea who will eventually handle it. The exception therefore travels outward through the chain of calls that led to the throw. At each level, the runtime checks whether a handler is present. Only when the chain is exhausted with no matching handler does the exception reach the default behavior.

mainprogram startread_slotcalls check_indexcheck_indexthrowsdefault handlerreports on cerrcallscallsexception passes upexception passes upno handler found
When an exception is thrown inside a nested function call and no handler catches it, which functions are unwound, in what order, and where does control finally land?

In the diagram, the exception starts in check_index and moves outward one call at a time: first into read_slot, then into main. Each function in the chain is left at the point of the throw, so the statements after the failing call in those functions never run. If main had a matching handler, the exception would stop there. Because the source places the default handler around main(), an exception that reaches main without a match is reported at that single outermost point.

The Default Handler and cerr

Definition

The default handler is the behavior that takes effect when an exception is not caught. It prints an error message on cerr, the standard error-diagnostic output stream. The default handler ensures that, whenever an exception is not caught, an error message appears on cerr.

What do you think happens?

When an exception thrown in a nested call is never caught, where does the default error message go?

  • standard output (cout)

  • cerr, the standard error-diagnostic stream

  • a file named error.log

  • nowhere, because the error is silent

Reveal answer
Answer:

cerr, the standard error-diagnostic stream

The default handler prints its error message on cerr. This is the reporting path a learner can rely on without writing a handler for every possible failure.

The key point is that a learner does not have to remember to write a handler for every possible failure in order to get some visible report of it. The default handler is a safety net: without it, an unhandled error would have no defined consequence that a programmer could rely on.

Why the Default Handler Lives in main()

The default exception handlers are described in connection with main(), the function where a C++ program begins. The reasoning is straightforward. Handlers can be placed around any region of code, but a default that catches anything still uncaught needs to exist at the outermost level, so that no failure escapes without a trace. Placing the default around main() gives the whole program one place where leftover exceptions are reported.

Catching Versus Reporting

The sample below is an original illustration of the mechanism. A function check_index throws an exception when an index is out of range, and read_slot calls it before reading an element. In the first version, nothing catches the exception. In the second version, main places a handler around the risky call so the program can respond in its own way.

Before
int main() {
    std::vector<int> values{10, 20, 30};
    read_slot(values, 7);   // nothing catches the exception
    std::cout << "after the call\n";
}

The exception has no matching handler, so the default handler reports it on cerr and the second line is never reached.

After
int main() {
    std::vector<int> values{10, 20, 30};
    try {
        read_slot(values, 7);
    } catch (const std::runtime_error& e) {
        std::cout << "recovered: " << e.what() << "\n";
    }
    std::cout << "after the call\n";
}

The matching handler takes over, prints its own message, and execution continues to the line after the try block.

Tracing a Throw Through Three Functions

python
Output (expected)
20
(the default handler writes an error message on cerr; the exact text depends on the implementation)
Notes on the code
  • check_index is the point where the exception is thrown when the index is too large
  • read_slot calls check_index first, so the throw interrupts it before v[i] is evaluated
  • The call read_slot(values, 7) has no handler in any function, so the exception reaches the default handler
  • The line printing not reached is skipped because control left main at the uncaught throw
Worked example

Following the uncaught exception step by step

In the program above, what happens when read_slot(values, 7) runs?

  1. 1
    Step 1

    read_slot calls check_index with index 7. The vector has size 3, so the condition i >= v.size() is true.

  2. 2
    Step 2

    check_index throws a runtime_error. Control leaves check_index immediately.

  3. 3
    Step 3

    The exception moves out of read_slot. The line that would print v[i] never runs.

  4. 4
    Step 4

    The exception moves into main. No catch clause matches it, so it reaches the default handler.

  5. 5
    Step 5

    The default handler prints an error message on cerr, and the line that prints not reached is not executed.

Result

The valid call printed 20. The invalid call printed nothing to standard output and produced an error message on cerr.

Range Checking an Index Before Access

The word range-checked describes the behavior you would expect: the vector verifies that an index falls within the valid range of positions before accessing an element. A valid index $i$ for a vector of size $n$ satisfies $0 \leq i \leq n-1$. Any index outside that interval is one that a check would reject.

index 0validindex 1validindex 2validindex 3validindex 4validindex 5out of range
Which index positions are valid for a vector of a given size, and which positions fall outside the range that a check would reject?

For a vector of size 5, the positions 0 through 4 are valid, and position 5 is the first position a check would reject. A check that throws on index 5 is exactly the kind of failure the default handler described earlier would report if nothing caught it.

Writing Your Own Checked Class or Using a Built-In Option

Writing exception handling around vector accesses often leads a programmer to define a checked version of vector, a wrapper that tests each index before using it. Some implementations of C++ spare you that work by supplying a range-checked version of vector already. According to the source, this is offered as a compiler option, so whether it is available depends on the implementation you are using.

Plain vector accessRange-checked vector access
The index is used directlyThe vector verifies that the index is within the valid range first
Protection against out-of-range access is not guaranteed in every implementationTrades a little extra checking for protection against out-of-range access
Available in every implementationOffered by some implementations, depending on the compiler option you use
A failure is reported only if the surrounding code happens to handle itA failed check throws an exception, and the default handler reports it if nothing catches it

Plain and range-checked vector access, as described in the source

Mistakes About Uncaught Exceptions

  • Common mistake

    Assuming an uncaught exception is silently ignored

    Why it fails

    The source states that the default handler ensures an error message appears on cerr whenever an exception is not caught.

    Do this instead

    Expect an error message on cerr when no handler matches, and design your reporting around that default.

  • Common mistake

    Treating the default handler as a recovery mechanism

    What it looks like

    Relying on the default handler to let the program continue after read_slot(values, 7) fails.

    Why it fails

    The default handler reports the failure. It does not resume the work that was interrupted at the throw.

    Do this instead

    Place a handler where you can respond sensibly, and keep the default as the last-resort report.

  • Common mistake

    Printing the error message to standard output instead of cerr

    Why it fails

    The default error message is routed to cerr, the standard error-diagnostic stream, which is separate from ordinary program output.

    Do this instead

    When you write your own diagnostics for failures, send them to cerr so they can be separated from normal output.

  • Common mistake

    Believing a range-checked vector exists in every C++ implementation

Predicting Where an Exception Lands

MEDIUM

A vector named readings has size 4. A function fetch calls check_index with the index 4, and fetch is called from main with no try block anywhere in the program. Which function in the chain receives the exception first, and where is the default error message written?

Hints
  • Valid indices for size 4 run from 0 to 3, so index 4 is rejected.
  • The exception leaves check_index first, then travels through fetch, then into main.
  • The default error message goes to cerr.

Key Takeaways

  • An exception that no handler catches reaches a default exception handler, which prints an error message on cerr.
  • The runtime searches for a matching handler, and an exception thrown in nested calls travels outward through them before reaching the default.
  • Placing the default handler around main() gives the program one defined place where leftover exceptions are reported.
  • Reporting an uncaught error is not the same as recovering from it, so catch the errors you expect.
  • A range-checked vector verifies that an index is within the valid range, and some implementations offer it as a compiler option.

Related concepts

More concepts starting with R