Basic Input and Output
Variables and Data Types

1.2 Basic Input and Output

In the previous lessons, we used std::cout to print strings to the screen. Now, let's explore how to read values from the user using std::cin and examine performance hacks when printing to the console (because no one likes slow applications, except maybe hourly billing consultants).

Reading Input with `std::cin`

Just as std::cout represents the standard output stream (the screen), std::cin represents the standard input stream (the keyboard).

To read input, we use std::cin along with the extraction operator >>. Think of the arrows as pointing to the right, showing that data flows from the keyboard into the variable.

#include <iostream>

int main() {
    std::cout << "Enter your age and height: ";
    
    int age{};
    double height{};
    
    // Read two variables sequentially from keyboard
    std::cin >> age >> height;
    
    std::cout << "You are " << age << " years old and " << height << "m tall.\n";
    return 0;
}
Reading integer and double inputs from the user.

When you run this program, it will pause and wait for the user to type. It will literally wait patiently until the heat death of the universe if they don't. The extraction operator automatically splits inputs based on whitespace (spaces, tabs, or newlines). If the user types 24 1.82 and hits Enter, 24 is extracted into age and 1.82 is extracted into height.

Reading Text: std::string Storage

To store text in C++, we use std::string (from the <string> header). A std::string is structurally different from primitive types. While primitive variables like int age fit entirely inside your CPU registers or stack memory frames (as raw values), a std::string is a small owning object on the stack (or in another container) whose internal layout is implementation-defined. Many standard libraries use Small String Optimization (SSO) to store short text inline without a heap allocation; longer strings may use a separate character buffer on the heap. Object size and inline capacity vary by platform and library, Lesson 6.4 covers SSO in depth.

#include <iostream>
#include <string>

int main() {
    std::string name{};
    std::cout << "Enter your name: ";
    std::cin >> name; // DANGER: extraction operator splits on whitespace!
    std::cout << "Hello, " << name << "!\n";
    return 0;
}
Reading text variables with std::cin.

The Whitespace Delimiter Trap and std::getline

When you run the code above and enter a single name like 'Alice', it works perfectly. But if you enter a full name like 'Alice Smith', the program prints 'Hello, Alice!'. What happened to Smith? Did they vanish into the void?

The extraction operator >> uses whitespace (spaces, tabs, newlines) as a delimiter. It reads characters from the hardware input stream buffer and stops the moment it hits a space. The remaining text 'Smith' is left floating inside the input stream buffer. If your program asks for another input later, it will immediately extract the leftover 'Smith' without waiting for you to type! (And you thought ghosts were only in uninitialized memory).

To read a full line of text including spaces, bypass the extraction operator and use std::getline. It consumes characters from the input stream buffer up to the newline character (\n), clearing the buffer cleanly and copying the text into your std::string:

#include <iostream>
#include <string>

int main() {
    std::string fullName{};
    std::cout << "Enter your full name: ";
    
    // Reads the entire line including spaces
    std::getline(std::cin, fullName);
    
    std::cout << "Hello, " << fullName << "!\n";
    return 0;
}
Reading whole lines of text using std::getline.

Handling Stream Fail States (Input Corruption)

What happens if the program asks for an integer age, but the user types a word like 'twenty'? In JavaScript or Python, this might evaluate to NaN or throw an exception. In C++, it corrupts the input stream.

When extraction fails, std::cin enters a fail state (setting a flag inside the stream), ignores all future input requests until you call clear() (your program will bypass all subsequent cin calls entirely!), and does not leave the target variable reliably unchanged. Since C++11, a failed arithmetic extraction typically writes 0 to the target (or the type's min/max on range errors). Never assume the old value survived, always re-validate or re-prompt after recovery.

To build resilient code, we must explicitly detect and clear stream fail states:

#include <iostream>
#include <limits> // Required for std::numeric_limits

int main() {
    int age{};
    std::cout << "Enter your age: ";
    
    while (!(std::cin >> age)) { // If extraction returns false (failed)
        std::cout << "Invalid input! Please enter a number (we know reading is hard): ";
        std::cin.clear();  // Clear the fail state flags
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Discard corrupted buffer
    }
    
    std::cout << "Age registered: " << age << '\n';
    return 0;
}
Safe input recovery with cin.

The Performance Pitfall: `std::endl` vs `\n`

When writing loops or high frequency prints, using std::endl can make your program significantly slower. This is a very common performance mistake that makes senior engineers twitch.

To understand why, you must understand how operating systems write text. Accessing the physical console is slow. To speed things up, the OS stores characters in an internal memory buffer first, and prints them in batches.

As I mentioned earlier, std::endl does two things:

  1. Inserts a newline character (\n) into the output stream.
  1. Flushes the stream buffer (forces the OS to immediately write the buffer contents to the screen).

Flushing the buffer forces a slow system call. If you are printing 100,000 lines of data and use std::endl on each line, your CPU will spend most of its time waiting for the physical output to complete. It's like calling a delivery driver to deliver one single french fry at a time. The driver will hate you, and so will your CPU.

Instead, if you use the character \n (or "\n"), C++ will buffer the output. The buffer will only flush when it fills up or when the program finishes, making execution drastically faster. It's like delivering the whole meal in one trip.

SLOW: Flushes the console buffer on every single iteration!
for (int i{0}; i < 100000; ++i) {
    std::cout << i << std::endl;
}

//  FAST: Buffers the output, flushing only when necessary.
for (int i{0}; i < 100000; ++i) {
    std::cout << i << '\n';
}
Fast printing vs slow printing in C++.

Competitive Programming Speed Hack

If you are ever writing code that processes millions of lines of input and output, the C++ standard library syncs itself with C's stdio library by default to allow mixing printf and cout. This synchronization adds overhead.

You can disable this synchronization at the top of your main function to make std::cin and std::cout just as fast as raw C functions:

int main() {
    // Turn off synchronization with C standard I/O library
    std::ios_base::sync_with_stdio(false);
    // Untie cin from cout (prevents automatic flushing before reading input)
    std::cin.tie(nullptr);

    // Your code here...
    return 0;
}
Optimizing I/O performance in main.

Only use this speed hack when you are sure you won't be mixing C style I/O functions (like printf and scanf) with C++ stream functions. Or else, your output will scramble like a jigsaw puzzle dropped down the stairs.

Finished reading this lesson?