Let's start by adjusting the program to check for errors.
#include <iostream>
using namespace std;
const int ArrSize = 400;
int main()
{
char arr1[ArrSize];
char arr2[ArrSize];
char arr3[ArrSize];
cout << "enter the first string ";
if (!(cin >> arr1))
{
cout << "Failed cin >> arr1\n";
}
cout << "enter the second string ";
if (!cin.get(arr2, ArrSize))
{
cout << "Failed cin.get(arr2, ArrSize)\n";
}
cout << "enter the third string ";
if (!(cin>>arr3))
{
cout << "Failed cin >> arr3\n";
}
cout << endl << endl;
cout << "first string is: " << arr1 << "\n";
cout << "second string is: " << arr2 << "\n";
cout << "third string is: " << arr3 << "\n";
return 0;
}
The results should be something like
enter the first string abc
enter the second string Failed cin.get(arr2, ArrSize)
enter the third string Failed cin >> arr3
first string is: abc
second string is:
third string is: <garbage here>
We can see that the second and third reads failed. Why is that? To find out, we need to do a little reading. Here's some high-quality documentation for std::istream::get
The relevant overload is number 3, but 3 just calls number 4 with the delimiter set to '\n'
and 4 says two important things,
Characters are extracted and stored until any of the following occurs:
count-1
characters have been stored
- end of file condition occurs in the input sequence (
setstate(eofbit)
is called)
- the next available input character c equals
delim
, as determined by Traits::eq(c, delim)
. This character is not extracted (unlike basic_istream::getline()
)
If no characters were extracted, calls setstate(failbit)
. In any case, if count>0, a null character (CharT() is stored in the next successive location of the array.
So if you only get a newline, delim in this case, the output string arr2
is null terminated and the stream is placed into fail state because no characters were extracted from the stream, making the stream unreadable until the failure is acknowledged by clearing it. This is what we are seeing: an empty string and fail bit.
Why is the string empty? Why didn't it prompt for input? Because cin >> arr1
reads one whitespace-delimited token from the stream. It will ignore all whitespace up to the start of the token, but it leaves the whitespace after the token in the stream.
If you type abc and hit enter, "abc\n" goes into the stream. cin >> arr1
reads "abc" into arr1
. The "\n" stays in the stream where cin.get(arr2, ArrSize)
finds it. The get
exit condition is immediately satisfied by the "\n", so get
stops and leaves the "\n" in the stream. No characters were extracted. Fail bit is set and arr2
is null terminated.
cin>>arr3
subsequently fails because you can't read from a failed stream. Nothing is placed in arr3
, so when arr3
is printed, it is unterminated and <<
keeps printing until it finds a terminator. This is the garbage characters, though technically anything can happen.
The question does not specify what is to be done with data left over after cin >> arr1
. Common solutions are to remove everything up to and including the newline character from the stream with
cin.ignore(numeric_limits<streamsize>::max(), '\n');
but if you want to use any characters left on the line for arr2
, you'll have to be trickier. For example, always read lines, build an istringstream
out of the line, and then parse the istringstream
as is done in option 2 of this answer.
Side note: Reading into character arrays with >>
is always risky because it will keep reading until whitespace is found. If the program reads the size of the array from the stream without finding whitepace, sucks to be you. get
knows to stop before its overflowed. >>
doesn't. On the other hand, get
will read until it finds the end of the line, not just a single whitespace delimited token.
>>
into a std::string
will do the right thing and resize the string
to fit the input. Generally prefer std::string
to char
arrays. And if you are using std::string
prefer std::getline
to get
or istream
's getline
.