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.
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.
char s1[] char s2[] char* s3 const char* s4 char* s5 char* s6 char s7[15] char s8[15] |
cout << s1 << endl; cout << s2 << endl; cout << s3 << endl; cout << s4 << endl; cout << s5 << endl; cout << s6 << endl; cout << s7 << endl; cout << s8 << endl; |
Hello world Hello world Hello world Hello world Hello world Hello world Example Example |
Furthermore, we can reexamine a familiar operation in the light of the previous discussion:
cout << "Please enter the value for x:" << endl;
While C-string output is predictable, input is not.
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:
#include <iostream> using namespace std; int main() { char input[100]; // C-string variable cout << "Enter a string" << endl; cin >> input; // does not read spaces cout << input << endl; return 0; }
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> using namespace std; int main() { char input[100]; cout << "Enter a string" << endl; cin.getline(input, 100); // reads spaces cout << input << endl; return 0; }
![]() | ||
#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) |