Module 1 · From C to Just-Enough C++
Fast Input and Output: Why Contest Code Starts the Same Way
In this lesson
- Write the two fast-input lines at the top of
main, and say what each one turns off. - Read until the input ends with
while (std::cin >> x), and read whole lines withstd::getlinewithout losing one to the leftover newline. - Choose
'\n'overstd::endlfrom a measurement, and explain why mixingprintfwithstd::coutafter the sync is off is a bug.
Kenji's program reads a million numbers and prints each one back. It is correct, yet on the judge it gets Time Limit Exceeded. Kenji, who optimises early, starts rewriting his loop. The loop is fine. The time goes into reading and printing, and two lines at the top of main fix it. This lesson measures how much.
A buffer, a sync and a tie
A buffer is a block of memory where characters wait. The program collects output there and hands it to the operating system in big batches, not one character at a time. Handing the waiting characters over is called a flush. Input works the same way: a big batch comes in, and reads take from it.
By default, C++ makes two promises that cost time. The first is the sync: std::cin and std::cout stay in step with C's stdin and stdout. Every character goes through C's input and output functions, so printf and std::cout can take turns safely. You pay for that safety on every character, even if you never call printf.
The second is the tie: std::cin is tied to std::cout, so std::cout is flushed before every read. At a keyboard, that makes a question like "Enter n:" appear before the program waits. A judge reads no questions.
The two fast-input lines
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::ios::sync_with_stdio(false);turns the sync off, so the C++ streams get buffers of their own.std::cin.tie(nullptr);cuts the tie, so a read no longer flushesstd::coutfirst.nullptris C++'s null pointer, the typed partner of C'sNULL.- Both go first in
main, before any input or output. - The price: from then on the program uses only
std::cinandstd::cout, neverscanforprintf.
The smallest program in the track's fixed shape: the two lines, read, compute, print with '\n'.
#include <iostream>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int price = 0;
int count = 0;
std::cin >> price >> count;
std::cout << price * count << '\n';
return 0;
}
100
That output is for the input 25 4. Every graded problem in this module starts with this shape.
So the first line gives C++ streams their own buffers, and the second stops the flush before every read.
std::endl is '\n' plus a flush
std::endl sends a newline and then flushes std::cout. '\n' only sends the newline. The characters that come out are the same.
#include <iostream>
int main()
{
std::cout << "first line" << std::endl;
std::cout << "second line" << '\n';
return 0;
}
first line
second line
You cannot see the flush in the output, only in the time. A flush is a trip to the operating system, and a trip per line adds up. std::endl is the right tool when a line must leave now. One case is an interactive judge waiting for your question. Another is a line you must see even if the program crashes a moment later. So the output is identical; std::endl only changes when it leaves.
Measured: a million numbers in and out Intermediate
Four programs read n = 1,000,000 and then a million integers, and print each one back on its own line. They differ only in the two lines and the line ending. Here is the fastest, with the timer around the work.
#include <chrono>
#include <cstdio>
#include <iostream>
int main()
{
std::freopen("out.txt", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n = 0;
auto start = std::chrono::steady_clock::now();
std::cin >> n;
for (int i = 0; i < n; i++) {
int x = 0;
std::cin >> x;
std::cout << x << '\n';
}
std::cout.flush();
auto stop = std::chrono::steady_clock::now();
long long ms = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
std::FILE* f = std::fopen("out.txt", "r");
std::fseek(f, 0, SEEK_END);
std::cerr << n << " numbers, " << std::ftell(f) << " bytes written, " << ms << " ms\n";
return 0;
}
Three details need a word. Compiler Explorer stops a program after 32 KB of output, so std::freopen sends stdout into a file, out.txt, before the streams start. The std::chrono lines read a clock before and after the loop; auto lets the compiler write the long type, and lesson 04 explains it. The last lines measure the file and print the result to stderr, the error stream.
The other three programs remove the two lines, or change '\n' to std::endl, or both. Each was run once on Compiler Explorer, x86-64 GCC 12.2, with the runner's command line, g++ -O2 -std=c++17. The input held a million integers from 0 to 99, ten per line, 2,893,323 bytes in all. Every program wrote the same 2,893,315 bytes.
| Program | The two lines | Line ending | Time, one run |
|---|---|---|---|
| A | no | std::endl | 750 ms |
| B | no | '\n' | 488 ms |
| C | yes | std::endl | 515 ms |
| D | yes | '\n' | 58 ms |
D, with both changes, took 58 ms against A's 750 ms, about 13 times faster. The middle two show why you need both. In C, the lines are in, but std::endl flushes once per line: a million trips to the operating system. In B, the line ending is '\n', but the tie still flushes std::cout before every read, and the program reads once per line.
Five more runs of each split the two lines apart, on the same site and input. The sync line alone, with '\n', took 433 to 2,158 ms, because the tie still flushes before every read. The tie line alone took 128 to 193 ms: no flush per read, but every character still goes through C's functions. Only with all three changes do reads and writes both work in big batches.
These runs share a busy machine, and the Playground's runner is another one. Re-runs moved A, B and C by hundreds of milliseconds, even swapping their order, while D stayed at 58 to 66 ms. Trust D's lead, not the exact milliseconds. So the two lines and '\n' belong together. Drop any one of the three, and the program ran at least twice as slow, in most runs over seven times.
Reading until the input ends: while (std::cin >> x)
Many inputs never say how many values are coming. You read until the input runs out. std::cin >> x gives back std::cin itself, which is how the chains of lesson 01 work. A stream used as a condition is true while its last read succeeded.
So while (std::cin >> x) reads a value, tests whether the read worked, and runs the body only for a value that really arrived. A read fails at the end of the input, or at something that is not a number. Then the loop ends. Zara counts the days below zero in her temperature log.
The newline between the two lines is just more space to >>, so the loop reads straight across it.
#include <iostream>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int x = 0;
int below = 0;
while (std::cin >> x) {
if (x < 0) {
below++;
}
}
std::cout << "days below zero: " << below << '\n';
return 0;
}
days below zero: 2
That output is for the input 4 -2 and 7 -5 on two lines. Zara tries an empty input first: the first read fails, the body never runs, and the program prints days below zero: 0.
At a keyboard you end the input yourself: Ctrl+D on Linux and macOS, or Ctrl+Z and then Enter on Windows. On the Playground, the input ends where the text in the input box ends. So the loop runs once per number that arrived, and the read that fails is the one that ends it.
Whole lines: std::getline and the leftover newline
>> stops at every space, so it cannot read a book title. std::getline(std::cin, line) reads every character up to the next newline into a std::string, spaces included. It throws the newline itself away. An empty line gives an empty string.
Like >>, it gives back the stream, so while (std::getline(std::cin, line)) reads line by line until the input ends. Now the trap. Maria's library input has a count on the first line and a title on the next.
#include <iostream>
#include <string>
int main()
{
int copies = 0;
std::string title;
std::cin >> copies;
std::getline(std::cin, title);
std::cout << "copies: " << copies << '\n';
std::cout << "title: [" << title << "]\n";
return 0;
}
copies: 3
title: []
That output is for the input 3 and The Little Prince on two lines. The title is empty. >> stopped right after the 3 and left the newline of that line in the stream. std::getline then read up to that newline: an empty line, which was the rest of the first one.
The fix is to finish the number's line before you read the next line. One more std::getline into a string you never use does it, and it also eats any spaces after the number.
Maria reads how many titles follow, then each title on its own line, and prints each with its number and length.
#include <iostream>
#include <string>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n = 0;
std::string rest;
std::string title;
std::cin >> n;
std::getline(std::cin, rest);
for (int i = 1; i <= n; i++) {
std::getline(std::cin, title);
std::cout << i << ". " << title << " (" << title.size() << " characters)\n";
}
return 0;
}
1. The Little Prince (17 characters)
2. Matilda (7 characters)
3. Treasure Island (15 characters)
That output is for the input 3, then the three titles on three lines. title.size() is the number of characters in the string, spaces counted. Remove the rest line and the titles shift by one: title 1 comes out empty.
So after >> reads the last number on a line, one std::getline finishes that line, and the next one reads a real line.
Walking a line one character at a time
A std::string can be read like a C array of char. line.size() is how many characters it holds, and line[i] is the character at index i, from 0. That is enough to find the words in a line, the way you would in C.
Here a word is a run of characters that are not spaces. A word starts at index i when line[i] is not a space, and either i is 0 or line[i - 1] is a space. That test ignores runs of two or more spaces, and spaces at the start or the end.
David shortens each title in his reading list to its initials. He reads line by line until the input ends.
#include <iostream>
#include <string>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string line;
while (std::getline(std::cin, line)) {
std::string initials;
for (int i = 0; i < (int)line.size(); i++) {
bool starts = line[i] != ' ' && (i == 0 || line[i - 1] == ' ');
if (starts) {
initials += line[i];
}
}
std::cout << initials << '\n';
}
return 0;
}
tlotr
atwied
atotc
That output is for four input lines: the lord of the rings, then around the world in eighty days with extra spaces, then an empty line, then a tale of two cities. The empty line still ran the loop once and printed an empty line. initials += line[i] adds one character to the end of a string. The (int) cast matches the type of i, because size() gives an unsigned count.
So a line is an array of characters you can walk, and a word starts where a non-space follows a space or the start.
printf and std::cout after the sync is off
Bob keeps writing scanf and printf, and sometimes he adds the two lines out of habit. With the sync off, each family keeps its own buffer, and nobody keeps them in order.
#include <cstdio>
#include <iostream>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::printf("1. printf\n");
std::cout << "2. cout\n";
std::printf("3. printf\n");
std::cout << "4. cout\n";
return 0;
}
Here is what two systems printed. Neither is the order in the code.
| Where it ran | Order of the lines |
|---|---|
| Compiler Explorer, x86-64 GCC 12.2 on Linux, runner's flags, three runs | 2, 4, 1, 3 every time |
A Windows machine, MinGW-w64 GCC 14.2, g++ -O2 -std=c++17, output to a pipe, two runs | 1, 3, 2, 4 both times |
Each buffer is emptied when the program ends, and which one goes first depends on the library underneath. So the same program can look right on your machine and come out scrambled on the judge. The rule is one family per program. If you want scanf and printf, use them everywhere and leave the two lines out; they are fast already. So once the sync is off, the order of mixed output is not yours to choose.
Numbers inside a string: a pointer at std::istringstream
Sometimes the numbers you want are already inside a std::string, such as one line from std::getline. std::istringstream, from the header <sstream>, is a stream that reads from a string instead of from stdin. >> works on it exactly as on std::cin.
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::string prices = "12 7 30";
std::istringstream in(prices);
int x = 0;
while (in >> x) {
std::cout << "price " << x << '\n';
}
return 0;
}
price 12
price 7
price 30
Module 3 teaches string streams properly. For now: any >> loop you can write for std::cin also works on a string.
Where this is used
- Codeforces interactive problems. Their statements tell you to flush after every query, with
fflush(stdout)orcout.flush(). A query left in the buffer never reaches the judge, and the run ends with Idleness limit exceeded. - The USACO Guide. Its page on fast input and output for C++ recommends this lesson's two lines, writing the second as
cin.tie(0). - nginx. The web server writes its access log with its own buffering, not with
printforstd::cout. Itsaccess_logdirective takesbuffer=andflush=settings, the same trade as'\n'againststd::endl.
Common mistakes
1. Looping on eof().
while (!std::cin.eof()) {
std::cin >> x;
if (x < 0) {
below++;
}
}
No message at either command line. For the input 4 -2 and 7 -5 on two lines, it printed days below zero: 3 on Compiler Explorer, one too many. After reading -5, the final newline is still unread, so eof() is still false. The next read fails, x keeps -5, and the body counts it again. Put the read in the condition: while (std::cin >> x). You will write the eof() version because it reads like English.
2. std::endl in a big loop.
for (int i = 0; i < n; i++) {
std::cin >> x;
std::cout << x << std::endl;
}
No message at either command line, only time. In the measurement above, this loop with the two lines took 515 ms; with '\n' it took 58 ms. Write '\n', and flush on purpose when a line must leave. You will reach for std::endl because old books and many tutorials end every line with it.
3. Bob's scanf after the two lines.
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> a;
std::scanf("%d", &b);
No message at either command line. For the input 3 4, Compiler Explorer printed a = 3, b = 0. The first >> filled std::cin's own buffer with the whole input, so scanf found nothing left to read. Use one family per program. Bob will do this whenever a scanf line is quicker to type than to change.
Zara's step counter does not say how many readings it saved. It just pours them out, some on one line and some on the next, until it runs out. Zara wants two numbers: the total of all the readings and how many there were.
Input. Zero or more integers, separated by spaces and newlines, until the end of the input.
Output. One line with two integers separated by one space: the sum of all the integers, then how many there were. With no integers at all, print 0 0.
Constraints. At most 80000 integers. Each integer is between -1000000000 and 1000000000. Time limit: 1 second per test.
Sample. Input 3 5 and -2 on two lines gives 6 3: 3 + 5 + (-2) = 6, from three integers.
#include <iostream>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
// Read integers until the input ends. Print their sum and how many
// there were, on one line, separated by one space.
return 0;
}
Graded as sum-until-end. The hidden tests include an empty input, an input of only blank lines, and 80000 integers.
Alice checks the length of her essay before she sends it. Count its lines and words with one std::getline loop.
Input. Zero or more lines of text, until the end of the input. A line may be empty or hold only spaces.
Output. One line with two integers separated by one space: the number of lines, then the number of words. A word is a run of characters that are not spaces.
Constraints. At most 1000 lines, each with at most 1000 characters and no tabs.
Sample. The three lines the cat sat, an empty line and on the mat give 3 6.
#include <iostream>
#include <string>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string line;
int lines = 0;
int words = 0;
// Read the input line by line with std::getline until it ends.
// Count every line, and every word inside every line.
std::cout << lines << ' ' << words << '\n';
return 0;
}
Not graded on its own. words-per-line, Exercise 3, grades reading lines with std::getline.
David is building an index for his notes. He needs every word with the number of the line it sits on. Some lines are empty, and some have extra spaces where he pressed the space bar twice.
Input. The first line holds one integer n. Then n lines of text follow. A line may be empty, may start or end with spaces, and may have several spaces between two words.
Output. One output line per word, in the order the words appear. Each holds the number of the text line the word is on (the first text line is 1), one space, then the word. A text line with no words prints nothing. If the whole text has no words, print nothing.
Constraints. 1 <= n <= 1000. Each text line has at most 1000 characters: letters, digits, punctuation and spaces, with no tabs. Time limit: 1 second per test.
Sample. Input 3, then the cat sat, an empty line and on the mat, gives six lines: 1 the, 1 cat, 1 sat, 3 on, 3 the and 3 mat. Text line 2 is empty, so it prints nothing; neither run of spaces in line 3 makes an empty word.
#include <iostream>
#include <string>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n = 0;
std::cin >> n;
// Read the n lines of text that follow with std::getline.
// For every word, print the number of its line (1 to n),
// one space and the word, one word per output line.
return 0;
}
Graded as words-per-line. The hidden tests include text lines that are empty or hold only spaces, a last line with no newline after it, and 1000 empty lines.
Every evening, each branch of Amara's bakery sends one line of sales. It holds one integer per sale, as many as it made, with refunds as negative numbers. A branch that sold nothing sends an empty line. Nobody says how many branches there are or how many sales are on a line.
Input. One or more lines, until the end of the input. Each line holds zero or more integers separated by spaces. Every line ends with a newline.
Output. One line per input line, in order, holding the total of that line (0 for an empty line). Then one last line: the word total, one space, and the total of every integer in the input.
Constraints. 1 to 20000 lines. At most 80000 integers in the whole input. Each integer is between -1000000000 and 1000000000. Time limit: 1 second per test.
Sample. The four lines 120 80 -20, 45, an empty line and 7 7 7 give 180, 45, 0, 21 and total 246 on five lines.
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
// Read the input line by line until it ends. Print the total of each
// line on its own line, then one last line: the word total, one
// space, and the total of every number in the input.
return 0;
}
Graded as line-totals. The hidden tests include an empty line, a line of only spaces, and 20000 lines.
Common doubts
Is
scanffaster thanstd::cin?Without the two lines, yes. With them, no longer by much. On the same million numbers, a
scanfandprintfversion took 95 ms in one run on Compiler Explorer, against 58 ms for program D. Either is fine if the whole program uses one family.What is in
xafterwhile (std::cin >> x)ends?Do not rely on it. On Compiler Explorer, after the input ended,
xstill held the last number, -5. When the input held the wordsnowin place of a number, the failed read stored 0 inx. Use the values inside the loop.Why not fix the leftover newline with
std::cin >> std::ws?std::wsskips all whitespace, so it works when the next line has text. But it also skips empty lines and the spaces at the start of the next line. When empty lines count, as in David's index, it loses them. The throwawaystd::getlinefinishes exactly one line.
Key takeaways
std::ios::sync_with_stdio(false);gives the C++ streams their own buffers, andstd::cin.tie(nullptr);stops the flush before every read.std::endlis'\n'plus a flush: write'\n', and flush on purpose when a line must leave now.- Measured once each on Compiler Explorer: 750 ms with neither change, 58 ms with both, for a million numbers in and out.
while (std::cin >> x)runs once per value that arrived and ends on the read that fails.std::getlinereads a whole line; after a>>, finish that line first.- Once the sync is off, never mix
printforscanfwithstd::coutorstd::cin.
Next, Maria's swap with pointers becomes a swap with no stars at all, and lesson 03 explains the & that now means something new.
End of lesson 2
Mark it done, and your progress moves with you.
Next: References: A Second Name for the Same Box