Fast C++ I/O for Coding Tests: What sync_with_stdio, cin.tie and endl Actually Do

Key takeaways

Two lines, ios::sync_with_stdio(false) and cin.tie(nullptr), plus writing '\n' instead of std::endl remove most iostream overhead in coding tests. Each one removes a guarantee, though: no more mixing with printf/scanf, and no automatic flush before reading, which matters for interactive problems.

A solution that is algorithmically fine can still hit a time limit when the input has hundreds of thousands of numbers, because the default settings of C++ iostreams do extra work on every operation. The usual fix is two lines at the top of main:

#include <iostream>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    // ... use only std::cin / std::cout from here on
}

Plus one habit: write '\n' instead of std::endl.

These are widely copied without explanation, and each of them removes a guarantee that some programs depend on. This page covers what each line does, what it breaks, and the input-parsing traps that tend to show up at the same time.

What sync_with_stdio(false) turns off

By default, the C++ standard streams (cin, cout, cerr, clog) are synchronized with the C streams (stdin, stdout, stderr). That guarantee means you can freely interleave printf and std::cout and the output appears in the order you wrote it. Implementations typically achieve it by making the C++ streams go through C stdio instead of keeping their own buffer, which adds overhead to every character or operation.

std::ios::sync_with_stdio(false) drops that guarantee and lets the C++ streams buffer independently. It is a static function, so it affects all standard streams, not just cin.

The cost is that mixing the two libraries is no longer ordered. This program, compiled with g++ 10.3 (MinGW) and run with output piped to a file:

#include <cstdio>
#include <iostream>

int main() {
    std::ios::sync_with_stdio(false);
    std::cout << "1 from cout\n";
    std::printf("2 from printf\n");
    std::cout << "3 from cout\n";
}

printed:

1 from cout
3 from cout
2 from printf

Each library flushed its own buffer at a different time. The exact order depends on the implementation and on whether output goes to a terminal, so do not reason about it; just pick one library per program.

Also call it before any I/O. If the program has already read or written through a standard stream, the effect of the call is implementation-defined.

What cin.tie(nullptr) turns off

A stream can be tied to an output stream, which means that stream is flushed before every input operation. By default std::cin is tied to std::cout. That is what makes this work in a console program:

std::cout << "Enter a number: ";   // no newline, no flush
int x;
std::cin >> x;                     // cout is flushed first, so the prompt appears

When a program alternates reading and writing, such as reading a query and printing its answer, the tie means one flush per read. std::cin.tie(nullptr) removes that, and output is only flushed when the buffer fills, when you flush explicitly, or at normal program exit.

endl vs ‘\n’

std::endl writes a newline and flushes the stream. '\n' only writes a newline. In a loop that prints one answer per line, endl turns buffered output into one flush per line, often a system call each time.

for (int x : answers) std::cout << x << '\n';   // buffered
// no explicit flush needed at the end: cout is flushed at normal exit

The size of the effect depends heavily on the platform, the standard library and whether output goes to a file or a terminal, so there is no single number to quote. The way to check it is to time a program that reads a million integers and echoes them, once with default settings, once with the two lines above, and once with '\n' replaced by std::endl; on typical judge toolchains the unsynchronized, untied version with '\n' is clearly the fastest, and std::endl alone can undo most of the gain. The code is short enough to re-run on your own judge’s toolchain.

getline after cin >>: the empty line

This is the most common input bug once people start mixing token and line input:

#include <iostream>
#include <string>

int main() {
    int n;
    std::string line;
    std::cin >> n;
    std::getline(std::cin, line);
    std::cout << "n=" << n << " first getline=[" << line << "]\n";
    std::getline(std::cin, line);
    std::cout << "second getline=[" << line << "]\n";
}

With input 3, newline, hello world:

n=3 first getline=[]
second getline=[hello world]

operator>> stops right after the digits and leaves the newline in the stream, so the first getline reads an empty line. Two fixes:

#include <limits>

std::cin >> n;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // drop the rest of the line
std::getline(std::cin, line);

// or: skip all leading whitespace, including blank lines
std::cin >> n >> std::ws;
std::getline(std::cin, line);

A bare std::cin.ignore() discards exactly one character. It works for the textbook case, but fails as soon as the line has trailing spaces or Windows \r\n line endings, because the one discarded character is not the newline. std::ws also skips blank lines, which is usually what you want in a coding test, but it would be wrong if an empty line is meaningful input.

On line endings: if an input file was produced on Windows, getline on a non-Windows system leaves a trailing '\r' in the string. When string comparisons fail on input that “looks identical”, that is the first thing to check.

Interactive problems: flush every query

In interactive problems the judge reads your output and only then sends the next input. If the query is still in your buffer, the judge waits for you and you wait for the judge until the time limit expires, and the verdict is usually reported as TLE or “idleness limit exceeded”, not as a wrong answer.

#include <iostream>
#include <string>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int lo = 1, hi = 1000000;
    while (lo < hi) {
        int mid = lo + (hi - lo + 1) / 2;
        std::cout << "? " << mid << std::endl;   // endl flushes: required here
        std::string reply;
        std::cin >> reply;                      // e.g. ">=" or "<", per the problem statement
        if (reply == ">=") lo = mid; else hi = mid - 1;
    }
    std::cout << "! " << lo << std::endl;
}

The protocol here is only an illustration; follow the one in the actual problem statement. The point is that every query ends with a flush. Using std::endl is fine in this case because the number of queries is small. Alternatively, leave cin tied to cout for interactive problems and the flush before each read happens automatically.

I have lost more time to this than I would like to admit: a personal template starts with cin.tie(nullptr), gets pasted into an interactive problem, and the solution hangs on the first query. It is an easy bug to miss because the same code works locally when you type the answers yourself in a terminal and the output happens to appear.

When the two lines are not enough

For very large inputs, a hand-written reader that pulls big chunks with fread and parses integers itself avoids the per-token overhead of formatted input entirely:

#include <cctype>
#include <cstdio>

static char buf[1 << 16];
static size_t len = 0, pos = 0;

inline int readChar() {
    if (pos == len) {
        len = std::fread(buf, 1, sizeof(buf), stdin);
        pos = 0;
        if (len == 0) return EOF;
    }
    return (unsigned char)buf[pos++];   // isdigit() needs a non-negative value
}

// Reads the next integer; returns false at end of input.
bool readInt(long long& out) {
    int c = readChar();
    while (c != EOF && c != '-' && !std::isdigit(c)) c = readChar();
    if (c == EOF) return false;
    bool neg = (c == '-');
    if (neg) c = readChar();
    long long x = 0;
    for (; c != EOF && std::isdigit(c); c = readChar()) x = x * 10 + (c - '0');
    out = neg ? -x : x;
    return true;
}

int main() {
    long long x, sum = 0, n = 0;
    while (readInt(x)) { sum += x; ++n; }
    std::printf("%lld numbers, sum %lld\n", n, sum);
}

With input 3, -5 10, 7:

4 numbers, sum 15

The getchar-based version of this reader that circulates in many templates often has a subtle issue: it returns 0 at end of input, which is indistinguishable from reading a real zero. Returning a success flag, as above, avoids that. This reader uses C stdio (fread), so do not combine it with std::cin on the same input.

Some templates also call std::cin.rdbuf()->pubsetbuf(...) to enlarge the input buffer. What that call does is implementation-defined and it may do nothing at all, so I no longer include it.

scanf/printf remain a reasonable choice too. Once iostreams are unsynchronized and untied, the difference between the two families is usually small compared with the time limit, so choose based on which one you write with fewer format bugs. The one thing to avoid is mixing them.

Minimal template

#include <bits/stdc++.h>   // GCC-specific convenience header; fine on most judges
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);          // remove this line (or flush every query) for interactive problems

    int n;
    cin >> n;
    vector<long long> a(n);
    for (auto& x : a) cin >> x;

    for (auto x : a) cout << x << '\n';
}

<bits/stdc++.h> is not a standard header and is not available with MSVC, so do not use it outside of contest code.