SovranCode
Modern C++ Lesson 18
Course contents50 available · 0 planned

Get a program running

01 · C++ Introduction: What Is C++?02 · How to Install a C++ Compiler and Set Up Your Editor03 · C++ Hello World: Your First Program Explained04 · How to Compile and Run C++ Programs05 · C++ Debugging: Warnings and Breakpoints

Work with values and input

06 · C++ Variables: Initialization and Scope07 · C++ Data Types: int, double and Type Conversion08 · C++ const, constexpr and auto Explained09 · C++ Operators: Arithmetic, Comparison and Logic10 · C++ User Input: cin, getline and Validation

Control flow and functions

11 · C++ Conditions: if, else if and else12 · C++ Switch Statement: Cases, break and default13 · C++ For Loop: Syntax and Examples14 · C++ While and Do-While Loops: Examples15 · C++ Functions: Parameters and Return Values

Strings and sequences

16 · C++ Strings: std::string with Examples17 · C++ Vector: std::vector with Examples18 · C++ Arrays: std::array and Bounds Checking19 · C++ Range-Based For Loop and Iterators20 · C++ Vector Practice: Build a Data Summary

Model data with classes

21 · C++ Structs and enum class Explained22 · C++ Classes and Objects: Encapsulation23 · C++ Constructors and Member Initialization24 · C++ Const Member Functions and Composition25 · C++ Copying Objects and the Rule of Zero

Lifetime and resource ownership

26 · C++ Object Lifetime, Destructors and RAII27 · C++ Pointers and References: nullptr and Borrowing28 · C++ unique_ptr: Smart Pointers and Ownership29 · C++ Move Semantics: std::move vs Copying30 · C++ string_view and span: Avoid Dangling Views

Containers and algorithms

31 · C++ map vs unordered_map: Lookup and Examples32 · C++ set, queue and stack: When to Use Each33 · C++ Lambda Expressions and STL Algorithms34 · C++ sort and binary_search: Rules and Complexity35 · C++20 Ranges: Views, filter and transform

Errors, files, and interfaces

36 · C++ optional and variant Explained37 · C++ Exceptions: try, catch and Safe Cleanup38 · C++ File Handling: Read and Write Text Files39 · C++ Input Parsing and Data Validation40 · C++ Polymorphism: Virtual Functions and Interfaces

Generic programming and quality

41 · C++ Templates: Function and Class Examples42 · C++20 Concepts and Template Constraints43 · C++ Unit Testing: Normal and Boundary Cases44 · C++ Sanitizers: Find Memory and Undefined Behavior Bugs45 · C++ Performance: Benchmark Before Optimizing

Build and deliver a complete application

46 · C++ Header Files and Translation Units47 · C++ CMake Tutorial: Build an Executable48 · C++ Dependencies and CTest: Automated Tests49 · C++ Multithreading: Threads and Data Races50 · C++ Command-Line Applications: Help and Release Checks
Modern C++50 available · 0 planned

Get a program running

01 · C++ Introduction: What Is C++?02 · How to Install a C++ Compiler and Set Up Your Editor03 · C++ Hello World: Your First Program Explained04 · How to Compile and Run C++ Programs05 · C++ Debugging: Warnings and Breakpoints

Work with values and input

06 · C++ Variables: Initialization and Scope07 · C++ Data Types: int, double and Type Conversion08 · C++ const, constexpr and auto Explained09 · C++ Operators: Arithmetic, Comparison and Logic10 · C++ User Input: cin, getline and Validation

Control flow and functions

11 · C++ Conditions: if, else if and else12 · C++ Switch Statement: Cases, break and default13 · C++ For Loop: Syntax and Examples14 · C++ While and Do-While Loops: Examples15 · C++ Functions: Parameters and Return Values

Strings and sequences

16 · C++ Strings: std::string with Examples17 · C++ Vector: std::vector with Examples18 · C++ Arrays: std::array and Bounds Checking19 · C++ Range-Based For Loop and Iterators20 · C++ Vector Practice: Build a Data Summary

Model data with classes

21 · C++ Structs and enum class Explained22 · C++ Classes and Objects: Encapsulation23 · C++ Constructors and Member Initialization24 · C++ Const Member Functions and Composition25 · C++ Copying Objects and the Rule of Zero

Lifetime and resource ownership

26 · C++ Object Lifetime, Destructors and RAII27 · C++ Pointers and References: nullptr and Borrowing28 · C++ unique_ptr: Smart Pointers and Ownership29 · C++ Move Semantics: std::move vs Copying30 · C++ string_view and span: Avoid Dangling Views

Containers and algorithms

31 · C++ map vs unordered_map: Lookup and Examples32 · C++ set, queue and stack: When to Use Each33 · C++ Lambda Expressions and STL Algorithms34 · C++ sort and binary_search: Rules and Complexity35 · C++20 Ranges: Views, filter and transform

Errors, files, and interfaces

36 · C++ optional and variant Explained37 · C++ Exceptions: try, catch and Safe Cleanup38 · C++ File Handling: Read and Write Text Files39 · C++ Input Parsing and Data Validation40 · C++ Polymorphism: Virtual Functions and Interfaces

Generic programming and quality

41 · C++ Templates: Function and Class Examples42 · C++20 Concepts and Template Constraints43 · C++ Unit Testing: Normal and Boundary Cases44 · C++ Sanitizers: Find Memory and Undefined Behavior Bugs45 · C++ Performance: Benchmark Before Optimizing

Build and deliver a complete application

46 · C++ Header Files and Translation Units47 · C++ CMake Tutorial: Build an Executable48 · C++ Dependencies and CTest: Automated Tests49 · C++ Multithreading: Threads and Data Races50 · C++ Command-Line Applications: Help and Release Checks
PREVIOUS LESSONC++ Vector: std::vector with Examples
NEXT LESSONC++ Range-Based For Loop and Iterators
C++ foundations · Lesson 18 About 37 min with practice

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.

Before you begin

Complete std::vector, integer conversions and validated input. Know the difference between an element count and the last valid index.

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.

A seven-position array has indices zero through six. A user supplies day one through seven; validation happens first, then subtracting one maps day three to index two. Day zero and day eight are rejected before any array access.
Validate the external domain before converting its numbering system to an internal index.

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.

C++ PRACTICE RUNNER

Observe partial initialization and value copying

The missing numeric initializers produce zero. Updating the copied array does not change the original integer elements.

#include <array>
#include <iostream>
int main() {
    const std::array<int, 4> original{4, 5};
    auto copy = original;
    copy.at(0) = 9;
    std::cout << "Size: " << original.size() << '\n';
    std::cout << "Tail: " << original.at(3) << '\n';
    std::cout << "Original: " << original.at(0) << '\n';
    std::cout << "Copy: " << copy.at(0) << '\n';
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

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.

C++ PRACTICE RUNNER

Inspect the first invalid index safely

The size is three, so index three is invalid. at throws; no unchecked invalid access is executed.

#include <array>
#include <iostream>
#include <stdexcept>
int main() {
    const std::array<int, 3> values{2, 4, 6};
    try {
        std::cout << values.at(3) << '\n';
    } catch (const std::out_of_range&) {
        std::cout << "Out of range\n";
    }
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

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.

LOCAL TERMINAL COMMANDS

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.exe
APPLY THE CONCEPT

Independent 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?
  1. Keep the parsed day signed until range validation succeeds.
  2. A valid day d maps to index d - 1.
  3. Compare day 1 and day 7 with the first and last declared readings.
C++ PRACTICE RUNNER

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.

#include <iostream>
#include <string>
#include <sstream>
#include <limits>

int main() {
    // Implement the acceptance checklist above.
    return 0;
}

TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

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
C++ PRACTICE RUNNER

Reference lab solution

Compare the reference with your own reasoning. Running this solution does not grade or complete the lesson.

#include <iostream>
#include <sstream>
#include <string>
bool readInteger(int& value) {
    std::string line;
    if (!std::getline(std::cin, line)) return false;
    std::istringstream parser{line};
    if (!(parser >> value)) return false;
    parser >> std::ws;
    return parser.eof();
}
#include <array>
int main() {
    const std::array<int, 7> readings{12, 13, 14, 15, 16, 17, 18};
    int day{};
    if (!readInteger(day)) { std::cout << "Invalid input\n"; return 1; }
    if (day < 1 || day > 7) { std::cout << "Invalid day\n"; return 1; }
    const auto index = static_cast<std::size_t>(day - 1);
    std::cout << "Day: " << day << "\nReading: " << readings.at(index) << '\n';
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

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.

  • C++ draft: array overview
  • Microsoft: array class
KNOWLEDGE CHECK

Check your understanding

Answer all five questions, then review the explanations. Answers are not saved or synced; completion is a separate self-review choice.

01When is std::array a suitable choice?
02What does array<int, 4> values{} establish?
03What index selects user-facing day seven?
04What is safe for array<int, 0>?
05Does converting an external negative index to size_t validate it?
PREVIOUS LESSONC++ Vector: std::vector with Examples
NEXT LESSONC++ Range-Based For Loop and Iterators
ON THIS PAGELearning objectivesChoose std::array when the size is part of the meaningBraces establish values; a declaration alone may notat() checks a boundary; [] assumes you proved itConvert one-based input only after checking its rangeFixed size does not eliminate empty-state or lifetime reasoningIndependent labReviewKnowledge checkReferences
Course overview