C++ Module 1 – Start here: toolchains, compiling and first programs
Console I/O in C++: cin, cout, print and println
Read numbers and whole lines with std::cin and std::getline, format output with C++23 std::print and std::println, and handle bad input and its end.
What you will learn
- Read numbers and whole lines from standard input
- Format output with std::print and std::println
- Avoid the getline-after-cin trap and detect bad input
Before you start
What you’ll learn · Before you start
What you will learn
- Read numbers and whole lines from standard input
- Format output with std::print and std::println
- Avoid the getline-after-cin trap and detect bad input
Before you start
On this page
A console program talks through three standard streams: it reads standard input, writes results to
standard output and writes problems to standard error. In C++, std::cin reads standard input,
std::cout writes standard output and std::cerr writes standard error, all from <iostream>. C++23 adds
std::print and std::println in <print>, which write formatted text in one call, the way Python’s f-strings or
C’s printf do. Use std::cin >> x to read numbers and words, std::getline to read a whole line, and
std::println for output that needs columns or a fixed number of decimals. This lesson also covers the two classic
input bugs: the empty line after >>, and input that is not a number.
The three standard streams and the C++ names for them
Text description of the diagram
The diagram runs from top to bottom.
- Standard input comes from the keyboard, or from a file or a pipe that the shell connects with < or |. The program reads it with std::cin >> and std::getline.
- The program writes its results with std::cout, std::print or std::println, which go to standard output: the screen, or a file or a pipe connected with > or |.
- It writes error messages with std::cerr or std::println(stderr, ...), which go to standard error: the screen, or a file connected with 2>.
A dashed arrow from std::cin to std::cout says that std::cin flushes std::cout before it waits for input, so a prompt written with std::cout is always visible first.
Two ways to write output
std::cout << writes each value in turn. std::println takes a format string with {} where the values go, and
ends the line for you; std::print does not add the line break:
#include <iostream>
#include <print>
int main() {
const int runs = 74;
const int balls = 51;
std::cout << "Asha scored " << runs << " off " << balls << " balls\n";
std::println("Asha scored {} off {} balls", runs, balls);
std::print("Strike rate: {:.2f}", 100.0 * runs / balls);
std::print("\n");
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra two_ways.cpp -o two_ways && ./two_ways
g++-16 -std=c++23 -Wall -Wextra two_ways.cpp -o two_ways && ./two_ways
""") Output
$ clang++ -std=c++23 -Wall -Wextra two_ways.cpp -o two_ways && ./two_ways Asha scored 74 off 51 balls Asha scored 74 off 51 balls Strike rate: 145.10 $ g++-16 -std=c++23 -Wall -Wextra two_ways.cpp -o two_ways && ./two_ways Asha scored 74 off 51 balls Asha scored 74 off 51 balls Strike rate: 145.10
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 two_ways_session.py
The first two lines came out the same: Asha scored 74 off 51 balls. The third shows why a format string helps:
{:.2f} printed the strike rate as 145.10, with two decimals, in one short expression. With std::cout the same
result needs std::fixed and std::setprecision(2) (the second from <iomanip>), and both stay set for every later
number.
To end a line, write '\n'. std::endl writes the line break and then flushes the stream, which forces the text
out to the screen or the file at once, and that costs time when it happens on every line:
#include <chrono>
#include <fstream>
#include <iostream>
int main() {
using clock = std::chrono::steady_clock;
const int lines = 1'000'000;
auto start = clock::now();
{
std::ofstream out("newline.txt");
for (int i = 0; i < lines; ++i) out << i << '\n';
}
auto newline_ms = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start).count();
start = clock::now();
{
std::ofstream out("endl.txt");
for (int i = 0; i < lines; ++i) out << i << std::endl;
}
auto endl_ms = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start).count();
std::cout << "'\\n': " << newline_ms << " ms\n";
std::cout << "std::endl: " << endl_ms << " ms\n";
} from session import run
# Both builds are optimised (-O2); the times differ from run to run.
run("""
clang++ -std=c++23 -O2 endl_cost.cpp -o endl_cost && ./endl_cost
g++-16 -std=c++23 -O2 endl_cost.cpp -o endl_cost && ./endl_cost
""") Output
$ clang++ -std=c++23 -O2 endl_cost.cpp -o endl_cost && ./endl_cost '\n': 114 ms std::endl: 5296 ms $ g++-16 -std=c++23 -O2 endl_cost.cpp -o endl_cost && ./endl_cost '\n': 79 ms std::endl: 5097 ms
This output changes from run to run: the times differ from run to run and from one computer to another
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 endl_session.py
Over repeated runs on this track’s build machine, an Apple M5 Pro, the million lines took 40 to 80 ms with '\n'
and 1.6 to 4.5 seconds with std::endl, 30 to 60 times as long, with both compilers. Flush only when the text must
appear right now, such as a progress message, and use '\n' everywhere else.
Formatting with std::println
A replacement field {} takes the next value; what goes after a colon inside it, the format specification,
sets its width, alignment, precision and base:
#include <print>
int main() {
const double pi = 3.14159265;
std::println("[{}]", pi);
std::println("[{:.2f}]", pi);
std::println("[{:8.3f}]", pi);
std::println("[{:<8}] [{:>8}] [{:^8}]", "left", "right", "centre");
std::println("[{:*^12}]", "four");
std::println("[{:+d}] [{:05d}] [{:#x}] [{:b}]", 42, 42, 255, 5);
std::println("{0} and {1}, then {0} again", "first", "second");
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra format_specs.cpp -o format_specs && ./format_specs
g++-16 -std=c++23 -Wall -Wextra format_specs.cpp -o format_specs && ./format_specs
""") Output
$ clang++ -std=c++23 -Wall -Wextra format_specs.cpp -o format_specs && ./format_specs [3.14159265] [3.14] [ 3.142] [left ] [ right] [ centre ] [****four****] [+42] [00042] [0xff] [101] first and second, then first again $ g++-16 -std=c++23 -Wall -Wextra format_specs.cpp -o format_specs && ./format_specs [3.14159265] [3.14] [ 3.142] [left ] [ right] [ centre ] [****four****] [+42] [00042] [0xff] [101] first and second, then first again
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 format_session.py
| Field | Printed (between the brackets) | What it does |
|---|---|---|
{} |
[3.14159265] |
The value in its shortest exact form |
{:.2f} |
[3.14] |
Two digits after the decimal point |
{:8.3f} |
[ 3.142] |
At least 8 characters wide; numbers are right-aligned |
{:<8}, {:>8}, {:^8} |
[left ], [ right], [ centre ] |
Left, right or centre within 8 characters |
{:*^12} |
[****four****] |
Centred, padded with * instead of spaces |
{:+d}, {:05d} |
[+42], [00042] |
Always show the sign; pad with zeros to 5 digits |
{:#x}, {:b} |
[0xff], [101] |
Hexadecimal with its prefix; binary |
{0}, {1} |
first and second, then first again |
Values by position, so one can be used twice |
Widths are what make tables line up. This scorecard sets each column’s width once, in the format string:
#include <print>
#include <string>
#include <vector>
struct Innings {
std::string batter;
int runs;
int balls;
int fours;
int sixes;
};
int main() {
const std::vector<Innings> card{
{"Asha", 74, 51, 8, 2},
{"Ravi", 9, 14, 1, 0},
{"Meera", 103, 88, 11, 3},
{"Kabir", 0, 2, 0, 0},
{"Zoya", 28, 19, 2, 2},
};
std::println("{:<8}{:>5}{:>6}{:>4}{:>4}{:>8}", "Batter", "R", "B", "4s", "6s", "SR");
int total = 0;
for (const auto& in : card) {
const double strike_rate = 100.0 * in.runs / in.balls;
std::println("{:<8}{:>5}{:>6}{:>4}{:>4}{:>8.2f}", in.batter, in.runs, in.balls, in.fours, in.sixes, strike_rate);
total += in.runs;
}
std::print("{:-<35}\n", "");
std::println("{:<8}{:>5}", "Total", total);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra scorecard.cpp -o scorecard && ./scorecard
g++-16 -std=c++23 -Wall -Wextra scorecard.cpp -o scorecard && ./scorecard
""") Output
$ clang++ -std=c++23 -Wall -Wextra scorecard.cpp -o scorecard && ./scorecard Batter R B 4s 6s SR Asha 74 51 8 2 145.10 Ravi 9 14 1 0 64.29 Meera 103 88 11 3 117.05 Kabir 0 2 0 0 0.00 Zoya 28 19 2 2 147.37 ----------------------------------- Total 214 $ g++-16 -std=c++23 -Wall -Wextra scorecard.cpp -o scorecard && ./scorecard Batter R B 4s 6s SR Asha 74 51 8 2 145.10 Ravi 9 14 1 0 64.29 Meera 103 88 11 3 117.05 Kabir 0 2 0 0 0.00 Zoya 28 19 2 2 147.37 ----------------------------------- Total 214
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 scorecard_session.py
The batter’s name is left-aligned in 8 characters ({:<8}) and every number is right-aligned in its own column, so the
strike rates 145.10, 64.29, 117.05, 0.00 and 147.37 end in the same column. The line of dashes is
{:-<35} with an empty value: 35 characters of fill. If a value is wider than its field, it is printed in full and
pushes the rest of the line to the right, so choose widths for the longest value you expect.
Text beyond English on Windows
std::print writes to the console through the operating system’s Unicode interface when the program’s text is
UTF-8, so characters such as é or € and text in scripts such as Devanagari or Tamil appear correctly in the Windows
console, which output through std::cout often does not. With Microsoft’s compiler, build with /utf-8 so that the
program’s string literals are UTF-8 (/utf-8 sets both the source and the execution character set). Most macOS and
Linux terminals use UTF-8 already.
Reading values with std::cin
std::cin >> x skips any spaces, tabs and line breaks, then reads as many characters as fit the type of x: digits
for an int, one word for a std::string. Several reads can be chained (std::cin >> a >> b), and it makes no
difference whether the values are on one line or several. Used as a condition, a read is true while it succeeds, so a
loop can read everything up to the end of the input:
#include <iostream>
#include <print>
int main() {
int runs = 0;
int total = 0;
int balls = 0;
while (std::cin >> runs) {
total += runs;
++balls;
}
std::println("{} runs off {} balls", total, balls);
} from session import run
run("""
cat over.txt
clang++ -std=c++23 -Wall -Wextra sum_runs.cpp -o sum_runs && ./sum_runs < over.txt
g++-16 -std=c++23 -Wall -Wextra sum_runs.cpp -o sum_runs && ./sum_runs < over.txt
""") Output
$ cat over.txt 1 4 0 6 2 1 $ clang++ -std=c++23 -Wall -Wextra sum_runs.cpp -o sum_runs && ./sum_runs < over.txt 14 runs off 6 balls $ g++-16 -std=c++23 -Wall -Wextra sum_runs.cpp -o sum_runs && ./sum_runs < over.txt 14 runs off 6 balls
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 sum_runs_session.py
The input 1 4 0, 6 2 and 1 spread over three lines gave 14 runs off 6 balls. The loop stops at the end of the
input, and it would also stop at the first word that is not a number. To end input typed at the keyboard, press
Ctrl-D (macOS and Linux) or Ctrl-Z and then Enter (Windows). To give a program a file as its input, as online judges
do, run ./sum_runs < over.txt.
Reading a whole line: the getline trap
std::getline(std::cin, line) reads everything up to the next line break, spaces included, and removes the line
break. That is how to read a full name. Mixed with >>, though, it fails in a way that surprises almost everyone:
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string name;
std::cout << "Age: ";
std::cin >> age;
std::cout << "Full name: ";
std::getline(std::cin, name);
std::cout << "\nHello, [" << name << "], age " << age << '\n';
} The input was 19 on one line and Asha Rao on the next, yet the program printed Hello, [], age 19: an empty
name. std::cin >> age read 19 and stopped at the line break after it, leaving that line break waiting.
std::getline then read up to the first line break, which came straight away, and returned an empty line. The
square brackets in the output are there to make the empty string visible.
The fix is to skip the leftover whitespace before getline. std::ws does exactly that:
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string name;
std::cout << "Age: ";
std::cin >> age;
std::cout << "Full name: ";
std::getline(std::cin >> std::ws, name);
std::cout << "\nHello, [" << name << "], age " << age << '\n';
} from session import run
# The same input file for both programs: an age on the first line, a full name on the second.
run("""
cat age_name.txt
clang++ -std=c++23 -Wall -Wextra age_then_name.cpp -o age_then_name && ./age_then_name < age_name.txt
g++-16 -std=c++23 -Wall -Wextra age_then_name.cpp -o age_then_name && ./age_then_name < age_name.txt
clang++ -std=c++23 -Wall -Wextra age_then_name_fixed.cpp -o age_then_name_fixed && ./age_then_name_fixed < age_name.txt
g++-16 -std=c++23 -Wall -Wextra age_then_name_fixed.cpp -o age_then_name_fixed && ./age_then_name_fixed < age_name.txt
""") Output
$ cat age_name.txt 19 Asha Rao $ clang++ -std=c++23 -Wall -Wextra age_then_name.cpp -o age_then_name && ./age_then_name < age_name.txt Age: Full name: Hello, [], age 19 $ g++-16 -std=c++23 -Wall -Wextra age_then_name.cpp -o age_then_name && ./age_then_name < age_name.txt Age: Full name: Hello, [], age 19 $ clang++ -std=c++23 -Wall -Wextra age_then_name_fixed.cpp -o age_then_name_fixed && ./age_then_name_fixed < age_name.txt Age: Full name: Hello, [Asha Rao], age 19 $ g++-16 -std=c++23 -Wall -Wextra age_then_name_fixed.cpp -o age_then_name_fixed && ./age_then_name_fixed < age_name.txt Age: Full name: Hello, [Asha Rao], age 19
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 age_session.py
std::getline(std::cin >> std::ws, name) printed Hello, [Asha Rao], age 19. std::ws also skips blank lines and
leading spaces, which is right for a name. If spaces at the start of a line matter, use
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); after the >> instead, which throws away only
the rest of that one line.
Both programs print Age: Full name: on one line because the input came from a file, so nothing typed was echoed. In a
terminal, your typing appears after each prompt. A prompt written with std::cout always shows before the program
waits, because std::cin flushes std::cout before every read (the standard ties the two streams together).
Detecting input that is not a number
When std::cin >> overs meets twenty, the read fails: overs is set to 0, the stream is marked as failed, and
twenty is left in the input. Every later read fails at once until the program deals with it. This loop asks until
it gets a number in range:
#include <iostream>
#include <limits>
#include <print>
int main() {
int overs = -1;
while (true) {
std::cout << "Overs (1-50): ";
if (std::cin >> overs && overs >= 1 && overs <= 50) {
break;
}
if (std::cin.eof()) {
std::println(stderr, "\nThe input ended before a valid number.");
return 1;
}
if (std::cin.fail()) {
std::println("\nNot a whole number (overs is now {}). Try again.", overs);
std::cin.clear();
} else {
std::println("\n{} is outside 1-50. Try again.", overs);
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
std::println("\nA {}-over match has {} balls per side.", overs, overs * 6);
} from session import run
# Built once with each compiler, then given two inputs: three answers, and a single wrong one.
run("""
clang++ -std=c++23 -Wall -Wextra read_overs.cpp -o read_overs && ./read_overs < overs.txt
./read_overs < overs_bad.txt
g++-16 -std=c++23 -Wall -Wextra read_overs.cpp -o read_overs && ./read_overs < overs.txt
./read_overs < overs_bad.txt
""") Output
$ clang++ -std=c++23 -Wall -Wextra read_overs.cpp -o read_overs && ./read_overs < overs.txt Overs (1-50): Not a whole number (overs is now 0). Try again. Overs (1-50): 75 is outside 1-50. Try again. Overs (1-50): A 20-over match has 120 balls per side. $ ./read_overs < overs_bad.txt Overs (1-50): Not a whole number (overs is now 0). Try again. Overs (1-50): The input ended before a valid number. [exit status 1] $ g++-16 -std=c++23 -Wall -Wextra read_overs.cpp -o read_overs && ./read_overs < overs.txt Overs (1-50): Not a whole number (overs is now 0). Try again. Overs (1-50): 75 is outside 1-50. Try again. Overs (1-50): A 20-over match has 120 balls per side. $ ./read_overs < overs_bad.txt Overs (1-50): Not a whole number (overs is now 0). Try again. Overs (1-50): The input ended before a valid number. [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 overs_session.py
With the input twenty, 75 and 20, it printed:
Not a whole number (overs is now 0). Try again., after whichstd::cin.clear()removed the failed state andstd::cin.ignore(…, '\n')threw away the rest of the bad line;75 is outside 1-50. Try again.: that read worked, so only the range check failed;A 20-over match has 120 balls per side.
With only ten on standard input, it printed the first message, then the input ended, so it wrote
The input ended before a valid number. to standard error and exited with status 1. Two mistakes make such loops
hang or spin forever: calling clear() without ignore(), which reads the same bad word again and again, and
forgetting the end-of-input check, after which every read fails at once.
Write error messages to standard error, with std::cerr << or std::println(stderr, …), as this program does. They
then stay on the screen even when a user saves the normal output with ./read_overs > result.txt, and they never get
mixed into results that another program reads.
Exercise: a marks report
Read a count n and then n whole-number marks from standard input, and print their average with two decimals and
the highest mark. For the input 5, then 72 85 64 and 91 57 on two more lines, the output must be exactly
Average: 73.80 and then Highest: 91, each on its own line. If the input ends early or holds something that is not a
number, print a message to standard error and exit with status 1.
Hint: read the count first and check it, then read the marks in a for loop, and divide as double, not as int.
One solution
#include <iostream>
#include <print>
int main() {
int n = 0;
if (!(std::cin >> n) || n <= 0) {
std::println(stderr, "First give how many marks follow (at least 1).");
return 1;
}
int total = 0;
int highest = 0;
for (int i = 0; i < n; ++i) {
int mark = 0;
if (!(std::cin >> mark)) {
std::println(stderr, "Expected {} marks, got {}.", n, i);
return 1;
}
total += mark;
if (i == 0 || mark > highest) {
highest = mark;
}
}
std::println("Average: {:.2f}", static_cast<double>(total) / n);
std::println("Highest: {}", highest);
} from session import run
run("""
clang++ -std=c++23 -Wall -Wextra -Wpedantic -Wconversion marks_report.cpp -o marks_report && ./marks_report < marks.txt
./marks_report < marks_short.txt
g++-16 -std=c++23 -Wall -Wextra -Wpedantic -Wconversion marks_report.cpp -o marks_report && ./marks_report < marks.txt
./marks_report < marks_short.txt
""") Output
$ clang++ -std=c++23 -Wall -Wextra -Wpedantic -Wconversion marks_report.cpp -o marks_report && ./marks_report < marks.txt Average: 73.80 Highest: 91 $ ./marks_report < marks_short.txt Expected 3 marks, got 2. [exit status 1] $ g++-16 -std=c++23 -Wall -Wextra -Wpedantic -Wconversion marks_report.cpp -o marks_report && ./marks_report < marks.txt Average: 73.80 Highest: 91 $ ./marks_report < marks_short.txt Expected 3 marks, got 2. [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 marks_session.py
It printed Average: 73.80 and Highest: 91. Given 3 and only two marks, it printed Expected 3 marks, got 2. to
standard error and exited with status 1.
Check yourself
Check yourself
5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
Interview questions
What is the difference between std::endl and '\n'? (warm-up)
Both end the line. std::endl then also flushes the stream: the standard defines it as writing a newline followed by
a call to flush(). Flushing forces the buffered text out to the terminal or the file, which costs a system call, so
std::endl in a loop is slow: writing a million lines to a file took 1.6 to 4.5 seconds with std::endl against
40 to 80 ms with '\n' on this track’s build machine. Use '\n' by default and flush deliberately when the text must
appear at once, for example a progress line or output that an interactive judge reads before it sends more input.
std::cerr flushes after every write anyway, and std::cin flushes std::cout before reading.
Key takeaways
std::cin,std::coutandstd::cerr(from<iostream>) read standard input and write standard output and standard error; C++23’sstd::printandstd::println(from<print>) format output in one call.- Inside
{},:.2fsets the decimals,:<8,:>8and:^8the alignment and width, and:#xand:bthe base. - Use
'\n', notstd::endl, to end lines: flushing every line was 30 to 60 times slower when writing a file. std::cin >> xskips whitespace and stops at the first character that does not fit;while (std::cin >> x)reads to the end of the input.- After
>>, a line break is still waiting: read lines withstd::getline(std::cin >> std::ws, line). - A failed read sets the value to 0 and the stream to failed;
clear()it,ignore()the bad line, and checkeof()so the loop cannot spin.
References
- P2093R14: Formatted output (ISO/IEC JTC1/SC22/WG21)
- Working Draft, Programming Languages: C++ (N4950) (ISO/IEC JTC1/SC22/WG21)
- /utf-8 (Set source and execution character sets to UTF-8) (Microsoft)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress