To this point, the console has been our primary source and target for input and output, and the extractor ( >> ) and the inserter ( << ) operators our primary means for performing the I/O operations. These operations also work with C-strings, but not always as we might expect. To better understand C-string I/O, we must consider the two operations separately.
C-string Output
The inserter operator works predictably well, independent of how the C-string was created (as an array or as a pointer). C++ behaves this way because the name of an array is a pointer, implying that the inserter operator sees no difference between the two representations. Furthermore, it makes no difference if the program creates the C-string as an automatic or local variable on the stack or a dynamic variable on the heap with new.
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Example
Example
C-string output to the console. The inserter operator prints the C-strings defined and initialized in the previous section to the console. These observations allow us to conclude that C-string's output is independent of how the program created and initialized it.
Furthermore, we can reexamine a familiar operation in the light of the previous discussion:
While C-string output is predictable, input is not.
C-string Input
The first step to reading data into a C-string is to define the C-string variable. We can define a C-string as a pointer or an array, so which definition is appropriate? If we define a C-string as a pointer, the pointer must point to memory allocated with the new operator or as an automatic array. Or we can initially create the C-string as an array. The latter choice is common, especially for simple input.
The next consideration is how big we should make the array. If we're lucky, the associated problem will inform us, but we're left on our own most of the time. If we make the C-string too small, the program may not have enough space to store all the data, but if it is too big, the program may waste space. Memory is typically plentiful unless working in a constrained environment (like a satellite or phone), so we try to identify a worst-case scenario and add a generous safety margin. We take that approach with the following simple program:
The "bigger boat" we need is another way to read C-strings. cin is an instance (i.e., an object) of a class named istream (short for input stream). The inserter operator, <<, is a member function defined in istream, and it is this function that fails to read white-space characters. Fortunately, istream also defines another member function named getline. getline reliably reads all characters, including spaces and tabs. It reads an entire line - everything up to and including the new-line character. Furthermore, it discards the new-line character, which saves us from any errors that might arise from a leftover new-line character remaining in the input stream, and it keeps us from discarding the new-line with a call to ignore. The next version of the program works the way most of us would expect:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int i1;
cout << "Enter the first number: ";
cin >> i1;
char s1[100];
cout << "Enter the first string: ";
cin.getline(s1, 100);
cout << s1 << endl;
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int i1;
cout << "Enter a number: ";
cin >> i1;
cin.ignore();
char s1[100];
cout << "Enter the first string: ";
cin.getline(s1, 100);
cout << s1 << endl;
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char s1[100];
cout << "Enter the first string: ";
cin.ignore();
cin.getline(s1, 100);
cout << s1 << endl;
return 0;
}
(a)
(b)
(c)
Revisiting the newline problem with C-strings.
We first encountered the newline problem while exploring single-character input with the get function. Unfortunately, the getline function exhibits the same problem. As the user types characters on the keyboard, the system puts them in the input stream (cin). The user signals the program to read the characters by pressing the Enter key, which appends a newline at the end of the characters in the stream. The pictures illustrate the input stream during the read operations.
The first input statement (light blue) reads an integer from the input stream but leaves the trailing newline character (i). The getline function (pink) reads and discards the leading newline character (ii), ending the call without reading a string.
The cin statement (light blue) reads an integer but leaves the newline character in the input stream (i). The ignore function (pink) discards the leading newline (ii), allowing the getline (yellow) to read the string successfully.
The behavior of example (b) tempts to adopt a simple strategy: always call ignore (pink) before getline (light blue). This simple program demonstrates that the strategy doesn't always work. Without a newline left from a previous input operation (i), the input stream looks similar to (ii) but without the leading newline. Therefore, the the ignore function discards the first character of the string, H, and the program saves and prints ello.