C++ / CONSOLE AND FILE INPUT-OUTPUT
iostream, cout, and formatted output
Print values with std::cout, control how the << chain converts each type to text, and reason about the stream's sticky format state and output buffering.
What you will learn
- Chain insertions with << knowing each one returns the same ostream reference
- Predict output from an operand's static type, not its value (char vs int)
- Flip a sticky flag like std::boolalpha and expect it to affect every later insertion
- Use '\n' by default and endl or flush() only when bytes must leave the buffer
Understanding iostream, cout, and formatted output
Including <iostream> gives you a handful of already-constructed global stream objects: std::cout, std::cerr and std::clog for output, std::cin for input, plus wide-character twins. std::cout is not a keyword or a print statement; it is an object of type std::ostream that owns a buffer wired to the process's standard output. The << next to it is an ordinary overloaded function whose left operand is the stream and whose right operand's static type selects which conversion to characters runs. Every one of those overloads returns std::ostream&, so std::cout << a << b groups as ((std::cout << a) << b) and reads in the same order the characters come out.
Formatted output means those << overloads consult settings stored inside the stream object while turning a value into characters: numeric base, floating-point notation and precision, field width, fill character, and whether a bool appears as 1/0 or true/false. Because the settings live in the object rather than in the call, they persist between statements and between functions: inserting std::boolalpha once changes every bool printed afterwards until something changes it back. The counterpart is unformatted output, put and write, which copies bytes and consults none of that state.
Characters you insert do not go straight to the terminal; they land in the stream's buffer and are handed to the operating system when the buffer fills, when you flush, when the program exits normally, or when std::cin reads (cin is tied to cout, so a prompt appears before the read blocks). std::endl writes '\n' and then flushes, which is why using it on every line of a large output is measurably slower than '\n' — you are asking for a write per line. std::cerr sits at the other extreme: it has unitbuf set and flushes after each insertion, so diagnostics survive a crash but can appear out of order relative to buffered cout when both are redirected into the same file.
<iostream>
int main() {
int count = 3;
double ratio = 0.5;
char grade = 'B';
bool ok = true;
std::cout << "count=" << count << " ratio=" << ratio << '\n';
std::cout << "grade=" << grade << " code=" << static_cast<int>(grade) << '\n';
std::cout << "ok=" << ok << '\n';
std::cout << std::boolalpha; // sticky: every later bool prints as a word
std::cout << "ok=" << ok << '\n';
std::cout << "sum=" << count + 1 << '\n';
std::cout.flush(); // hand the buffered bytes over now
}
std::cout is an ordinary object and << is an ordinary function that returns that object, converting each value using format state the stream carries between statements.
Worked examples
Teaching << about your own type
Shows that chaining works only because each insertion hands the stream back.
<iostream>
struct Point {
int x;
int y;
};
std::ostream& operator<<(std::ostream& os, const Point& p) {
return os << '(' << p.x << ", " << p.y << ')';
}
int main() {
Point a{1, 2};
Point b{-4, 7};
std::cout << "a=" << a << " b=" << b << '\n';
std::ostream& out = std::cout; // cout is just an object
out << "same stream\n";
}
Example explained
Line 1operator<< is a free function because the left operand has to be the stream, not your class.
Line 2Returning os is the whole mechanism behind chaining: the outer std::cout << " b=" needs a usable stream back from the previous insertion.
Line 3Inside the body, os << '(' and os << p.x reuse the library's char and int overloads, so Point formatting is assembled from existing pieces.
Line 4std::ostream& out = std::cout compiles because cout is a plain global object, and out writes to exactly the same buffer.
Static type decides how a value prints
Contrasts the character and integer overloads, and formatted output with put/write.
<cstdint>
<iostream>
int main() {
std::uint8_t level = 65;
char letter = 'Z';
std::cout << "level: " << level << '\n';
std::cout << "level: " << +level << '\n';
std::cout << "letter: " << letter << " code: " << static_cast<int>(letter) << '\n';
std::cout.put('R');
std::cout.write("aw\n", 3);
}
Example explained
Line 1std::uint8_t is unsigned char on every mainstream implementation, so the character overload wins and 65 prints as A.
Line 2Unary + promotes level to int, which selects the integer overload and prints the digits 65.
Line 3static_cast<int>(letter) does the same for a plain char: 'Z' is code 90 in ASCII.
Line 4put and write are unformatted output — they push bytes into the buffer and ignore format state completely.
Why << needs parentheses so often
Demonstrates that the insertion operator binds tighter than comparison, bitwise and conditional operators.
<iostream>
int main() {
int n = 7;
std::cout << (n > 5 ? "big" : "small") << '\n';
std::cout << (n < 10) << '\n';
std::cout << (n & 1) << '\n';
std::cout << n * 2 << '\n';
}
Example explained
Line 1Without parentheses, std::cout << n > 5 ? "big" : "small" groups as (std::cout << n) > 5, and the compiler complains about comparing an ostream instead of about your condition.
Line 2(n < 10) yields a bool, printed as 1 because boolalpha is off by default.
Line 3& binds looser than <<, so std::cout << n & 1 would try to bitwise-and the stream itself.
Line 4* binds tighter than <<, which is why n * 2 needs no parentheses at all.
Important notes
std::cin is tied to std::cout, so a prompt is flushed automatically just before a >> read even without endl; nothing flushes it before a long computation, a sleep, or a read from a socket.
Almost all format settings are sticky, with field width the exception: it resets to 0 after each insertion, which is why column layouts must reapply it every time (covered with the iomanip controls).
Common mistakes
Printing a byte-sized integer and expecting digits: with std::uint8_t count = 65, std::cout << count selects the character overload and prints A, so counters and byte values silently appear as letters or invisible control characters. Write +count or static_cast<int>(count).
Concatenating as in Python or Java: std::cout << "n = " + n is pointer arithmetic on the literal, not concatenation, so it walks n characters into or past the string and prints garbage or crashes. Use a second << instead.
Ending every line with std::endl in a loop that writes thousands of lines, forcing a flush per line and making the program several times slower; the mirror-image mistake is assuming '\n' output already reached the terminal, then losing it when the program aborts before the buffer drains.
Try it yourself
Change, predict, then run
In a browser editor, print a char and a std::uint8_t holding 65 both as characters and as numbers in one << chain, then print the comparison grade == 'A' twice — once after inserting std::boolalpha and once after std::noboolalpha — and confirm the same expression comes out as true and then as 1.
Open the C++ workspaceCheck your understanding
With unsigned char b = 65; the statement std::cout << b << ' ' << b + 0 << '\n'; prints "A 65". What explains the difference between the two insertions?
- cout inspects the value: 65 falls in the printable ASCII range so it prints a letter, and b + 0 pushes it outside that range
- unsigned char has no operator<< overload, so the stream falls back to writing the raw byte
- The overload is chosen from the operand's static type: unsigned char selects the character overload, while b + 0 is an int and selects the integer overload
- The + operator resets the stream's format state, so the following insertion switches to numeric form
Show answer
Overload resolution happens at compile time from the declared type of the operand, and integer promotion in b + 0 turns it into an int. The first option is tempting because 65 does happen to be printable, but cout never looks at the value: unsigned char b = 200 would emit a non-ASCII byte rather than the digits 200.