Why does this work? Using cin to read to a char array smaller than given input

31,079

Solution 1

You are writing past the bounds of the array. The C++ standard doesn't say this should be an error; it says it is undefined behaviour. This means anything can happen, including seemingly working correctly. Simply put, your code does not have well-defined behaviour and so you shouldn't trust it to work.

We can imagine why it's probably working though. The first 15 characters will fit nicely into the array:

|H|o|w|C|a|n|I|P|o|s|s|i|b|l|y|F|i|t|T|h|i|s|E|n|t|i|r|e|S|t|r|i|n|g|I|n|?|...
^                             ^
|    These characters fit     |
         in the array

The rest of the characters are being written to the following memory locations. Now, remember that the null character, which is used to terminate C-style strings, is defined to have a representation that is all 0 bits. Now if the location following the location that contains the ? has all 0 bits in it, the string will appear to be null-terminated.

But the fact is, this is undefined. It just happens to work. Unfortunately, this is the scariest type of bug because it can seemingly work for a long time until one day you start getting calls from your very, very angry client.

Solution 2

You could use istream::get with the buffer and the size of the buffer:

cin.get(name1, Size);

As others have noted, it's far easier to use std::string:

std::string name1;
cin >> name;
Share:
31,079
Admin
Author by

Admin

Updated on March 27, 2020

Comments

  • Admin
    Admin about 4 years

    I'm reading C++ Primer Plus (6th Edition) and I've come across some sample code in chapter 4 which I have a question about:

    Listing 4.2 strings.cpp

    // strings.cpp -- storing strings in an array
    #include <iostream>
    #include <cstring> // for the strlen() function
    int main()
    {
        using namespace std;
        const int Size = 15;
        char name1[Size]; // empty array
        char name2[Size] = "C++owboy"; // initialized array
        // NOTE: some implementations may require the static keyword
        // to initialize the array name2
        cout << "Howdy! I'm " << name2;
        cout << "! What's your name?\n";
        cin >> name1;
        cout << "Well, " << name1 << ", your name has ";
        cout << strlen(name1) << " letters and is stored\n";
        cout << "in an array of " << sizeof(name1) << " bytes.\n";
        cout << "Your initial is " << name1[0] << ".\n";
        name2[3] = '\0'; // set to null character
        cout << "Here are the first 3 characters of my name: ";
        cout << name2 << endl;
        return 0;
    }
    

    The code itself doesn't cause any confusion, but I've been running it through and I'm confused by a certain scenario.

    name1 is initialised as an array of chars 15 elements in length - am I right in thinking this should hold a string 14 characters in length? The end char should be reserved for the string terminator, right?

    If I enter my name as HowCanIPossiblyFitThisEntireStringIn?, I get the following output:

    Howdy! I'm C++owboy! What's your name?

    HowCanIPossiblyFitThisEntireStringIn?

    Well, HowCanIPossiblyFitThisEntireStringIn?, your name has 37 letters and is stored

    in an array of 15 bytes.

    Your initial is H.

    Here are the first 3 characters of my name: C++

    How is the entire name I enter being stored? If I step through the code, after cin reads into name1, Visual Studio tells me it contains elements 0 - 14, with the last one being the char 'y' ("HowCanIPossibly...). I would assume from this that any extra data entered had been truncated and lost, but this is obviously not the case as the following cout successfully writes the entire name out to the console.

    For curiosity's sake, could anyone enlighten me as to what's happening here? For the record, I'm using Visual Studio 2012 Express.