C++ for C Developers | Lesson 2: Beyond printf: C++ I/O Streams & std::string

Goal: Learn how to read input using std::cin, understand the benefits of C++ stream I/O over printf/scanf, and get introduced to the powerful std::string class for handling text.

1. Reading Input with std::cin

Just as std::cout handles output, std::cin (from <iostream>) handles standard input (usually the keyboard). We use the stream extraction operator (>>) to get data from the stream.

C++
#include <iostream>
#include <string> // Need this header for std::string

int main() {
    int age;
    double weight;
    std::string name; // Using the C++ string class! (More on this soon)

    // Prompt the user for their age
    std::cout << "Please enter your age: ";
    std::cin >> age; // Read an integer from input into the 'age' variable

    // Prompt the user for their weight
    std::cout << "Please enter your weight (e.g., 75.5): ";
    std::cin >> weight; // Read a double from input into 'weight'

    // Prompt the user for their first name
    std::cout << "Please enter your first name: ";
    std::cin >> name; // Read a sequence of non-whitespace characters into 'name'

    // Display the collected information
    std::cout << "\n--- You entered ---\n";
    std::cout << "Name: " << name << std::endl;
    std::cout << "Age: " << age << std::endl;
    std::cout << "Weight: " << weight << std::endl;

    return 0;
}


How cin >> ... Works:

  • It skips leading whitespace (spaces, tabs, newlines).
  • It reads characters from the input buffer until it encounters whitespace or input that doesn’t match the type of the variable it’s trying to read into.
  • It attempts to convert the read characters into the type of the variable (e.g., int, double, std::string).
  • Like cout <<, you can chain cin >>: std::cin >> var1 >> var2;
flowchart TD
    A[User types input] --> B[Input Buffer receives characters]
    B --> C{Is there leading whitespace?}
    C -- Yes --> D[Skip whitespace]
    C -- No --> E[Read input based on variable type]
    E --> F{Successful conversion?}
    F -- Yes --> G[Assign value to variable]
    F -- No --> H[Enter error state]

2. cin/cout vs. scanf/printf

Why use this over the familiar scanf and printf?

Featureprintf/scanf (C-style)cout/cin (C++ Streams)
Type SafetyNo. Requires correct format specifiers (%d, %f, %s). Mismatch leads to undefined behavior.Yes. Type is determined by the variable. Safer.
ExtensibilityDifficult to add support for custom types.Easier. Can overload << and >> for user-defined types (classes).
SyntaxFormat string + variable list. Error-prone.Uses operators (<<, >>). Often cleaner for basic types.
Header<cstdio> (or <stdio.h>)<iostream>

Example of Type Safety Issue in C:

C
int x;
printf("Enter a number: ");
scanf("%f", &x); // WRONG specifier! Trying to read float into int address. Undefined Behavior!

C++ avoids this:

C++
int x;
std::cout << "Enter a number: ";
std::cin >> x; // Reads an integer. If user types "abc", cin enters an error state.
               // If user types "12.5", it likely reads 12 and leaves ".5" in the buffer.

classDiagram
    class CStyle_IO {
        - scanf()
        - printf()
        + Format Strings
        - Type Unsafe
        - Less Extensible
    }

    class CppStyle_IO {
        - std::cin
        - std::cout
        + Stream Operators (<<, >>)
        + Type Safe
        + Custom Type Overload
    }

    CStyle_IO <|-- InputOutput
    CppStyle_IO <|-- InputOutput

3. The Problem with cin >> string and Whitespace

Notice in the first example, we asked for the first name. What happens if you enter “John Doe”?

std::cin >> name; will only read “John” into the name variable. The ” Doe” part will be left in the input buffer, potentially causing issues for subsequent cin calls.

4. Reading Entire Lines: std::getline

To read a whole line of text, including spaces, until the user presses Enter, use the std::getline function (defined in the <string> header).

C++
#include <iostream>
#include <string> // Need this header!

int main() {
    std::string fullName;
    int id;

    std::cout << "Enter your ID: ";
    std::cin >> id;

    // *** Important: Consume the leftover newline character ***
    // After 'cin >> id;', the user pressed Enter, leaving '\n' in the buffer.
    // std::getline reads until '\n', so we need to get rid of the one left by cin.
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Clears buffer up to newline

    std::cout << "Enter your full name: ";
    std::getline(std::cin, fullName); // Read the entire line into fullName

    std::cout << "\n--- Entered Data ---" << std::endl;
    std::cout << "ID: " << id << std::endl;
    std::cout << "Full Name: '" << fullName << "'" << std::endl; // Quotes show extent

    return 0;
}

(Note: std::numeric_limits requires #include <limits>)

Key Takeaway: Be careful when mixing cin >> ... and std::getline. The cin >> ... often leaves the newline character (\n) in the input buffer, which getline then immediately reads as an empty line. Use cin.ignore(...) to discard the leftover newline before calling getline.

5. Introducing std::string

In C, you work with char arrays and pointers (char*) for strings. This involves manual memory management (using malloc/free for dynamic strings), careful bounds checking to avoid buffer overflows, and using functions from <string.h> (like strcpy, strcat, strlen).

C++ offers the std::string class (from <string>) which makes string manipulation much easier and safer.

graph LR
    A["C-style string (char*)"]
    B["std::string"]

    A --> A1["Manual memory allocation (malloc / free)"]
    A --> A2["Functions from string.h (e.g., strcpy, strlen)"]
    A --> A3["Risk of buffer overflows"]

    B --> B1["Automatic memory management"]
    B --> B2["Rich member functions (length, find, replace)"]
    B --> B3["Safer, bounds-checked access"]
    B --> B4["Seamless integration with streams"]

Benefits of std::string:

  • Automatic Memory Management: It handles its own memory. No need for malloc/free for the string data itself. It grows automatically as needed.
  • Rich Functionality: Provides many built-in member functions for common operations (finding substrings, replacing characters, getting length, etc.).
  • Safer: Reduces risks of buffer overflows compared to fixed-size char arrays or improper use of strcpy/strcat.
  • Integration with Streams: Works seamlessly with cout << and cin >> (and getline).

Basic std::string Usage:

C++
#include <iostream>
#include <string> // Don't forget this!

int main() {
    // Declaration and Initialization
    std::string s1;             // Default constructor: empty string ""
    std::string s2 = "Hello";   // Initialize from string literal
    std::string s3(" World");   // Initialize with constructor syntax
    std::string s4(s2);         // Copy constructor: s4 is now "Hello"
    std::string s5(5, 'x');     // Creates "xxxxx" (5 copies of 'x')

    // Concatenation (joining strings)
    std::string message = s2 + s3 + "!"; // Uses the '+' operator -> "Hello World!"
    message += " How are you?";          // Append using '+=' -> "Hello World! How are you?"

    // Getting Length
    std::cout << "Message: " << message << std::endl;
    std::cout << "Length: " << message.length() << std::endl; // or message.size()

    // Comparison
    std::string name1 = "Alice";
    std::string name2 = "Bob";
    if (name1 == "Alice") {
        std::cout << "name1 is Alice" << std::endl;
    }
    if (name1 < name2) { // Lexicographical comparison (like strcmp)
        std::cout << name1 << " comes before " << name2 << std::endl;
    }

    // Accessing Characters (like arrays, but safer ways exist)
    // char firstChar = message[0]; // 'H' - similar to C arrays
    // std::cout << "First char: " << firstChar << std::endl;
    // Note: Direct index access doesn't do bounds checking by default.
    // message.at(index) is a safer alternative that throws an error if out of bounds.

    return 0;
}

Summary of Lesson 2:

  • Use std::cin >> variable; to read formatted input.
  • C++ stream I/O is generally type-safer than scanf/printf.
  • The extraction operator >> stops at whitespace.
  • Use std::getline(std::cin, string_variable); to read an entire line including spaces.
  • Remember to handle the leftover newline when mixing cin >> and getline.
  • Include the <string> header to use std::string.
  • std::string provides automatic memory management, easy concatenation (+), length (.length()), comparison (==, <, >), and many other useful operations, making it far superior to C-style strings for most tasks.

Next Lesson: We’ll explore enhancements C++ brings to functions, including function overloading and default arguments, allowing for more flexible and readable function definitions.

More Sources:

cppreference – std::string: https://en.cppreference.com/w/cpp/string/basic_string

Learn C++ – std::string Tutorial: https://www.learncpp.com/cpp-tutorial/stdstring/

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top