C++ Arrays: std::array and Bounds Checking
Learn fixed-size C++ std::array, value initialization, checked access, signed input validation and safe one-based to zero-based index conversion.
What you will learn
- Choose std::array for a count that belongs in the type.
- Initialize every numeric element before reading it.
- Translate a validated one-based day into a zero-based index.
- Distinguish checked access from unchecked preconditions.
- Explain empty arrays and the limits of fixed-size storage.
Choose std::array when the size is part of the meaning
Seven daily readings are not merely a vector that happens to contain seven values today. If a record always contains seven positions, std::array<int, 7> puts that fixed count into its type. Include <array>, choose the element type, and give the count as a compile-time value. The object owns those seven integers together. This is useful when a missing or extra position would mean the record has a different shape.
An array does not grow with push_back and does not have a reserve operation. That is a deliberate constraint rather than a missing convenience. If the number of readings is chosen at runtime, use an appropriate dynamic collection such as vector instead. Do not use a compiler-specific variable-length built-in array and assume you have written portable C++20. The distinction between fixed shape and variable count should come from the problem, not a preference for short syntax.
std::array<int, 7> and std::array<int, 5> are different types. Passing either by value copies its integer elements, while a const reference can let a function observe the specific array without that copy. A function that accepts one extent does not automatically accept every other extent. General interfaces arrive later with templates and views; an explicitly seven-element interface is a clear starting contract when the domain actually requires seven positions.
Braces establish values; a declaration alone may not
Write std::array<int, 7> readings{} when every element should start at zero. For numeric elements, value initialization through these braces provides meaningful initial values. A local std::array<int, 7> readings; without an initializer does not give you seven reliable zeros. Do not inspect uninitialized values to discover what your machine happens to display. Make the initialization visible in the declaration and test the behavior you intended to establish.
An initializer with fewer values than the extent fills the remaining integer elements through the aggregate initialization rules. For example, {4, 5} in a four-element integer array gives four, five, zero and zero. That is convenient only when zero is a valid default in your domain. If zero means a genuine reading, using it to stand for missing data would conflate two states. Later optional-value modeling provides a better representation for absent measurements.
The extent is not the final index. A seven-element array has size seven and final valid index six. This remains true when the element type is a larger record instead of int. Avoid using sizeof as an element count: it concerns object size in bytes, not the number of logical readings. Prefer size() for the sequence count and keep byte-oriented questions separate from the application's indexing rules.
Observe partial initialization and value copying
The missing numeric initializers produce zero. Updating the copied array does not change the original integer elements.
Predict the output, edit the source, then select Run code.
Compare with the starter's expected output
Size: 4
Tail: 0
Original: 4
Copy: 9
After editing inputs, calculate a new prediction; this reference describes only the starter.
at() checks a boundary; [] assumes you proved it
Access with operator[] requires an index inside the array's extent. C++20 does not automatically turn a violated precondition into a friendly message. A valid-looking result on one run would not make the access correct. The checked at member throws std::out_of_range for an invalid position, allowing the program to handle that particular failure. Neither operation understands that the user meant day seven rather than internal position seven.
The example catches that specific exception to make a boundary visible without executing undefined behavior. Include <stdexcept> explicitly for its type. We do not depend on the exact wording of the implementation's exception message, which can vary among toolchains. The stable output is our own Out of range label. This is a narrow demonstration, not a full exception-handling strategy; the later error-handling unit develops recovery contracts in more depth.
Use validation for expected bad input and keep checked access as an additional guard where it suits the design. A user typing eight for a seven-day report should receive Invalid day, not an implementation-specific exception transcript. Establishing the domain before access also makes your program's control flow easier to test: rejection must happen before any element is used, regardless of whether the library offers a checked accessor.
Inspect the first invalid index safely
The size is three, so index three is invalid. at throws; no unchecked invalid access is executed.
Predict the output, edit the source, then select Run code.
Compare with the starter's expected output
Out of range
After editing inputs, calculate a new prediction; this reference describes only the starter.
Convert one-based input only after checking its range
The lab displays days one through seven because that is the interface a person sees. Internally, the array uses positions zero through six. Parse the complete input line into an int first, so a negative value remains available for an ordinary negative check. Reject values below one or above seven. Only then calculate day minus one and convert it to std::size_t for indexed access.
A cast does not validate the value it converts. Converting minus one to an unsigned index type can produce a very large number; subtracting from an already unsigned zero can also wrap. Neither operation repairs the user's request. Keep the proof in a readable order: parse, validate the external domain, translate, access. A reviewer should be able to identify the reason every access is safe without mentally reversing several casts.
Avoid an off-by-one repair that merely changes a comparison until the example seems to work. Write the allowed endpoints explicitly and test each neighbor: day zero must fail, day one must select the first element, day seven must select the last, and day eight must fail. Then test malformed text separately from a syntactically valid but out-of-range day. These cases discriminate parsing errors from indexing-policy errors.
Fixed size does not eliminate empty-state or lifetime reasoning
std::array<int, 0> is a valid standard-library type, unlike relying on a zero-length built-in array extension. Its size is zero, so a traversal visits no elements and begin equals end. There is no valid first or last element. Do not call front or back on it and do not infer a usable element from data(). A pointer value, if obtained, is not evidence that an element exists to read.
An array's fixed extent prevents resizing but does not make every reference immortal. A reference to an element becomes unusable when its owning array's lifetime ends. Returning a reference to a local array element would therefore be wrong even though no reallocation ever occurred. Keep ownership, extent and lifetime as separate questions. They become especially important when functions begin accepting borrowed views of collections later in the course.
For this lab, the seven initialized values are known records and the input selects a day; input does not modify the array. Declare the collection const to express that boundary. Report the day label and its reading only after validation. A clean compiler build cannot establish that your numbering convention is correct, so retain transcripts for all seven valid selections and both invalid adjacent endpoints as part of your test evidence.
Compile and test outside the lesson
The compiler-backed editors support complete single-file programs when the execution service is available. Otherwise save one program as main.cpp and run these commands with your configured compiler. Supply all standard input before Run; this is not an interactive terminal.
macOS / Linux (choose your installed compiler):
clang++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
# Or: g++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
./lesson
Windows Developer Command Prompt:
cl /std:c++20 /EHsc /W4 main.cpp /Fe:lesson.exe
lesson.exeIndependent lab
Select a reading from a fixed seven-day record without confusing day numbers with array indices.
Acceptance checklist
- Store seven initialized readings, 12 through 18, in a const std::array.
- Read one complete integer day line.
- Report Invalid input and status 1 for malformed, missing or overflowing input.
- Report Invalid day and status 1 for a day outside 1 through 7.
- Validate before subtracting one and converting to an index; print the selected day and reading.
Need a hint?
- Keep the parsed day signed until range validation succeeds.
- A valid day d maps to index d - 1.
- Compare day 1 and day 7 with the first and last declared readings.
Write and run your lab
Build your own solution before revealing the reference. Use the input field for normal, boundary and invalid cases; this runner executes your program but does not grade it.
Predict the output, edit the source, then select Run code.
Your lab evidence and notes
Record predictions, actual output, commands and the cause of each repair. Code is plain text: this scratchpad does not execute or grade C++.
Loading local notes…
Review your acceptance checklist and quiz explanations before marking this lesson complete. This is self-review, not a grade or certification.
Reveal the explained lab solution
Reference lab solution
Compare the reference with your own reasoning. Running this solution does not grade or complete the lesson.
Predict the output, edit the source, then select Run code.
Expected output
Day: 3
Reading: 14
Braced initialization provides all seven values and const prevents accidental changes to this lookup record.
Whole-line parsing distinguishes malformed text from a well-formed but unsupported day number.
Only a validated day is translated to an index. The first and last successful positions are zero and six.
Review before moving on
Explain the lesson's objectives in your own words, compare your lab evidence with the acceptance checklist, and keep the first failing case alongside your repair. Completing a page is not proof that every C++ feature or input is supported.
- Choose std::array for a count that belongs in the type.
- Initialize every numeric element before reading it.
- Translate a validated one-based day into a zero-based index.
- Distinguish checked access from unchecked preconditions.
- Explain empty arrays and the limits of fixed-size storage.
References and further reading
Original examples and exercises accompany these primary references. Tool-specific diagnostics and support depend on your installed version.