dhein
dhein

Reputation: 6555

Unexpected behaving of cin.clear();

What I want to do:

I want a code storing names into an array of char*

My intention to use cin.getline and cin.clear is to make the input error safe.

The rules are: It has to be stored up to 10 names or to interupt after a . is entered.

In both cases after that the names have to put on cout 1 Name per line.

What my Loop should do: take up to MAX_NAME_LEN letters and everything what extends the max len should get discarded fon the stdin before the next Loop starts.

do
{
    cin.getline(szInputRef, MAX_NAME_LEN);
    sizeLastLen = cin.gcount();
    szpNames[sizeIndex] = new char[sizeLastLen];
    strncpy (szpNames[sizeIndex], szInputRef, sizeLastLen);
    szpNames[sizeIndex][sizeLastLen] = '\0';
    sizeIndex++;
    cin.clear();
}
while ((sizeIndex < 10) &&(szpNames[sizeIndex - 1][0] != '.'));

for (sizeIndex = 0; sizeIndex < 10; sizeIndex++)
{
    if (szpNames[sizeIndex][0] == '.')
    {
        break;
    }

    cout << szpNames[sizeIndex] << endl;
}

But what actually happens: the Input that extends the number of MAX_NAME_LEN letters gets taken as next Input without the check. So imagine we say for testcase MAX_NAME_LEN is 3

and have a input of:

ig
se
nols

teh Output would be:

ig
se
nol
s

And in the case the input is > (MAX_NAME_LEN * 2) the app crashes as for the second input the checks seem to not appear. So after all reading about the documentation to cin.clear()

I cant understand why the input even exist anymore to get saved after the call to cin.clear(). And not even any Clou of the behaving of extending input gets stored without the cin.getline check so the app can Crash.

Could anyone explain what I am understanding wrong or what Logical mistake is in my code?

Upvotes: 0

Views: 234

Answers (1)

seldon
seldon

Reputation: 510

Discarding everything that's in the input stream is not generally a useful thing to do. Someone could feed you a file to stdin, for example, in which case you'd discard everything, or your OS might buffer differently than you expect and you'd end up with unpredictable results. What you really want to do is to ignore the rest of the current input line. The easiest way to do this is to say

#include <iostream>
#include <limits>

std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

Where std::numeric_limits<std::streamsize>::max() is the largest number in the std::streamsize type and a special case for std::istream::ignore where it means infinity. What this means is: Ignore infinitely many characters or to the next '\n', whichever comes first.

However, you can't do this unconditionally because std::istream::getline consumes the newline if the buffer is large enough; you'd ignore every other line. You have to execute it only if the line was too long to fit into the buffer. When this happens, std::istream::getline sets failbit, so we end up with

if(std::cin.fail()) {
  std::cin.clear();
  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

By the way, you should also guard against unrecoverable I/O errors and "errors" such as end-of-file. Currently, if someone presses Ctrl+D (Unix) or Ctrl+Z (Windows) while your program runs, it will do funny things. Of course, that is much easier if you use std::string and std::getline.

Upvotes: 1

Related Questions