What & How & Why

Chapter.3

第三章的习题答案


Ex3.1-3.10

ex.3.1
Exercise 3.1: Rewrite the exercises from § 1.4.1 (p. 13) and § 2.6.2 (p. 76) with appropriate using declarations.

CODE:Q.1 Q.2 Q.3 Q.4 Q.5 Q.6

ex.3.2
Exercise 3.2: Write a program to read the standard input a line at a time. Modify your program to read a word at a time.

#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main(int argc, char const *argv[])
{
	//read a line at time
	string s;
	while (getline(cin,s)) {
		cout << s << endl;
	}
	//read a word at time
	string s2;
	while (cin >> s2) {
		cout << s2 << endl;
	}
	return 0;
}

ex.3.2
Exercise 3.3: Explain how whitespace characters are handled in the string input operator and in the getline function.

* In the string input, whitespace characters will be the end sign of the print, that is, string input will save whatever before the first whitespace character and end the print.

  • In the getline function, whitespace character will be deemed as a plain character, except the \n. Getline function will print whatever it has before the \n.
ex.3.3
ex.3.4
Exercise 3.4: Write a program to read two strings and report whether the strings are equal. If not, report which of the two is larger. Now, change the program to report whether the strings have the same length, and if not, report which is longer.

/*comparing the value*/
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;

int main(int argc, char const *argv[])
{
	string s1,s2;
	cout << "Enter two strings: " << endl;
	cin >> s1 >> s2;

	if(s1 == s2) {
		cout << "Two strings are equal." << endl;
	}
	else {
		cout << (s1 > s2 ? s1:s2) << " is greater." << endl;
	}
	return 0;
}
/*comparing the length*/
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;

int main(int argc, char const *argv[])
{
	string s1,s2;
	cout << "Enter two strings: " << endl;
	cin >> s1 >> s2;

	if(s1.size() == s2.size()) {
		cout << "Two strings have the same length." << endl;
	}
	else {
		cout << (s1.size() > s2.size() ? s1:s2) << " is longer." << endl;
	}
	return 0;
}

ex.3.5
Exercise 3.5: Write a program to read strings from the standard input, concatenating what is read into one large string. Print the concatenated string. Next, change the program to separate adjacent input strings by a space.

/*concatenate all string inputs in one
 */
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;

int main(int argc, char const *argv[])
{
	string str, largeStr;

	while(cin >> str) {
			largeStr += str;
	}
	cout << largeStr << endl;
	return 0;
}
/*
 *concatenate all string inputs, 
 *separate each adjacent input by a space
 */
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main(int argc, char const *argv[])
{
	string str, largeStr;
	while(cin >> str) {
		largeStr = largeStr + str + " ";
	}
	cout << largeStr << endl;
	return 0;
}

ex.3.6
Exercise 3.6: Use a range for to change all the characters in a string to X.

#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;

int main(int argc, char const *argv[])
{
	string s ("Hello World.");
	for(auto &c : s) {
		c = 'X';
	}
	cout << s << endl;
	return 0;
}

ex.3.7
Exercise 3.7: What would happen if you define the loop control variable in the previous exercise as type char? Predict the results and then change your program to use a char to see if you were right.

The result is going to identical with what we have in the original string. Usually, if we want to manipulate the data in the string, we use reference to access and modify it. In this case, if we use char type instead of char& type, when executing the statement c='X',we actually created a new copy c of the character that we want to modify, and the modification here is all on that copy and it will not affect the original string. Thus, we are just outputting the original content of the string。

ex.3.8
Exercise 3.8: Rewrite the program in the first exercise, first using a while and again using a traditional for loop. Which of the three approaches do you prefer and why?

#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main(int argc, char const *argv[])
{
	string s("Hello World!!");
	//while ver
	decltype(s.size()) curStrSub = 0;
	while (curStrSub < s.size()) {
		s[curStrSub] = 'X';
		++curStrSub;
	}
	cout << s << endl;
	s = "Hello World!!";
	//normal for type
	for(decltype(s.size()) curStrSub2 = 0;
		curStrSub2 < s.size(); ++curStrSub2 ) {
		s[curStrSub2] = 'Y';
	}
	cout << s << endl;
	return 0;
}
For some sort of traversal work like this, I prefer to use Range For statement to process it. There are two advantages: On the one hand, we can avoid using subscript to access and modify the character unit if using the Range For statement. Using reference to manipulate the data is much safer and would prevent us away from calculating the length of the whole string. On the other hand, Range For statement provided a much cleaner form for the traversal. It makes the program more readable.

e.x.3.9
Exercise 3.9: What does the following program do? Is it valid? If not, why not?

string s;
cout << s[0] << endl;
The program is trying to print out the first character of the string s, however, the behavior is undefined since s is an empty string.

e.x.3.10
Exercise 3.10: Write a program that reads a string of characters including punctuation and writes what was read but with the punctuation removed.

#include <iostream>
#include <string>
#include <cctype>
using std::string;
using std::cin;
using std::cout;
using std::endl;

int main(int argc, char const *argv[])
{
	string s1;
	getline(cin,s1);
	for (auto c : s1) {
		if (!ispunct(c)) {
			cout << c;
		}
	}
	cout << endl;
	return 0;
}

Ex.3.11-3.20

ex.3.11
Exercise 3.11: Is the following range for legal? If so, what is the type of c?

const string s = "Keep out!";
for (auto &c : s) { /* ... */ }
Yes, it is legal. The type of c is a reference that refers to const char. Notice the c in this program is read-only. Any modification that tries to update the value that c refers to is going to be prevented.

ex.3.12
Exercise 3.12: Which, if any, of the following vector definitions are in error? For those that are legal, explain what the definition does. For those that are not legal, explain why they are illegal.

(a) vector<vector<int>> ivec; //legal, ivec holds vector that holds int elements.
(b) vector<string> svec = ivec; //illegal, copy initialization from a vector requires the same type as the target vector
(c) vector<string> svec(10, "null"); //legal, vector hold 10 elements with string value "null"

ex.3.13
Exercise 3.13: How many elements are there in each of the following vectors? What are the values of the elements?

(a) vector<int> v1; // no element
(b) vector<int> v2(10); //10 elements with value 0
(c) vector<int> v3(10, 42); //10 elements with value 42
(d) vector<int> v4{10}; // 1 element with value 10
(e) vector<int> v5{10, 42}; // two elements with value 10 and 42
(f) vector<string> v6{10}; // 10 empty strings
(g) vector<string> v7{10, "hi"}; //10 strings with value "hi"

ex.3.14
Exercise 3.14: Write a program to read a sequence of ints from cin and store those values in a vector.

#include <iostream>
#include <vector>
using std::vector;
using std::cin;
using std::cout;
using std::endl;
int main(int argc, char const *argv[])
{
	int i = 0;
	vector<int> vi;
	while (cin >> i) {
		vi.push_back(i);
	} 
	return 0;
}

ex.3.15
Exercise 3.15: Repeat the previous program but read strings this time.

#include <iostream>
#include <string>
#include <vector>
using std::vector;
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main(int argc, char const *argv[])
{
	string str;
	vector<string> vs;
	while (cin >> str) {
		vs.push_back(str);
	} 
	return 0;
}

ex.3.16
Exercise 3.16: Write a program to print the size and contents of the vectors from exercise 3.13. Check whether your answers to that exercise were correct. If not, restudy § 3.3.1 (p. 97) until you understand why you were wrong.

CODE:Q.1

ex.3.17
Exercise 3.17: Read a sequence of words from cin and store the values a vector. After you’ve read all the words, process the vector and change each word to uppercase. Print the transformed elements, eight words to a line.

CODE:Q.1

ex.3.18
Exercise 3.18: Is the following program legal? If not, how might you fix it?

vector<int> ivec;
ivec[0] = 42;
illegal. There is no way to use the subscript method to create a new element for the vector. Accessing a non-exist vector element through subscript would cause undefined behavior. We can fix it by either initializing the vector before manipulating the element or using push_back() to add a new element:
vector<int> ivec2 (10, 0);
ivec2[0] = 42;
vector<int> ivec3;
ivec3.push_back(42);

3.19
Exercise 3.19: List three ways to define a vector and give it ten elements, each with the value 42. Indicate whether there is a preferred way to do so and why.

//direct initialization
vector<int> ivec1(10, 42);
//list initialization
vector<int> ivec2{42,42,42,42,42,42,42,42,42,42,42,42};
//push_back
vector<int> ivec3;
for (int i = 0; i < 10; ++i) {
    ivec3.push_back(42);
}
It is recommended to use the first method since it is simple and easy. However, if elements in the vector are not identical and the size of the vector is unknown, then the third method is preferred because the first method is going to cause some sort of poor performance due to the implementation scheme of vector.

ex.3.20
Exercise 3.20: Read a set of integers into a vector. Print the sum of each pair of adjacent elements. Change your program so that it prints the sum of the first and last elements, followed by the sum of the second and second-to-last, and so on.

CODE: Q.1 Q.2

Ex.3.21-3.30

ex.3.21
Redo the first exercise from § 3.3.3 (p. 105) using iterators.

CODE:Q.1

ex.3.22
Revise the loop that printed the first paragraph in text to instead change the elements in text that correspond to the first paragraph to all uppercase. After you’ve updated text, print its contents.

Notes:To my CHAOS head: so we got a program in previous example:

for (auto it = text.cbegin();
it != text.cend() && !it->empty(); ++it)
cout << *it << endl;
The program itself actually did the work “print the first paragraph”. The only thing we need to do is just capitalize all the characters in each line(element) in the first paragraph. The level: paragraph→line→charactor.

CODE:Q.1

ex.3.23
Exercise 3.23: Write a program to create a vector with ten int elements. Using an iterator, assign each element a value that is twice its current value. Test your program by printing the vector.

CODE: Q.1

ex.3.24
Redo the last exercise from § 3.3.3 (p. 105) using iterators.

CODE: Q.1 Q.2

ex.3.25
Rewrite the grade clustering program from § 3.3.3 (p. 104) using iterators instead of subscripts.

CODE: Q.1

ex.3.26
Exercise 3.26: In the binary search program on page 112, why did we write mid = beg + (end - beg) / 2; instead of mid = (beg + end) /2;?

There is no adding operation between iterators.

ex.3.27
Exercise 3.27: Assuming txt_size is a function that takes no arguments and returns an int value, which of the following definitions are illegal? Explain why.

unsigned buf_size = 1024;
(a) int ia[buf_size]; //illegal, dimension must be const expression
(b) int ia[4 * 7 - 14]; //legal
(c) int ia[txt_size()]; //legal if the txt_size() function is a const expression
(d) char st[11] = "fundamental"; // illegal, the defination need at least 12 elements space for the initialization.

ex.3.28
Exercise 3.28: What are the values in the following arrays?

string sa[10]; //10 null strings
int ia[10]; // 10 zero
int main() {
    string sa2[10]; // 10 null strings
    int ia2[10]; // undefined
}

ex.3.29
Exercise 3.29: List some of the drawbacks of using an array instead of a vector.
  • The biggest drawback is Array has no flexibility.
  • It can be bug-prone since it uses subscript to access and modify data
  • It is not a part of STL data structure, hence it can't benefit from Generic Programming.
ex.3.30
Exercise 3.30: Identify the indexing errors in the following code:

constexpr size_t array_size = 10;
int ia[array_size];
for (size_t ix = 1; ix <= array_size; ++ix)
ia[ix] = ix;
In this program, the subscript range of the array is from 0 to 9. However, in its for loop, the subscript operation started from 1 and looped 10 times. The value of the last subscript was 10, which was definitely out of the subscript range.

Ex.3.31-3.40

ex.3.31
Exercise 3.31: Write a program to define an array of ten ints. Give each element the same value as its position in the array.

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

int main(int argc, char const *argv[])
{
	int ai[10] = {0};
	for (unsigned i = 0; i < 10; ++i) {
		ai[i] = i;
	}
	for (auto i : ai) 
		cout << i << " ";
	cout << endl;
	return 0;
}

ex.3.32
Copy the array you defined in the previous exercise into another array. Rewrite your program to use vectors.

CODE: Q.1 Q.2

ex.3.33
Exercise 3.33: What would happen if we did not initialize the scores array in the program on page 116?

Don't do this. All build-in type arrays where are default initialized inside a function are undefined.

ex.3.34
Exercise 3.34: Given that p1 and p2 point to elements in the same array, what does the following code do? Are there values of p1 or p2 that make this code illegal?

p1 += p2 - p1;
In C++, the precedence of compound assignment is much lower than subtraction. Thus, the above statement is actually like:
p1 = p1 + (p2 - p1);
The operation let p1 points to the emelent that p2 currently points, in other words, equal to p1 = p2.
Since p2 - p1 is going to return a ptrdiff_t type which can be used to “relocate” the p1, as long as both p1 and p2 are valid, the statement can be forever legal.
Ref: C++ Operator Precedence

ex.3.35
Exercise 3.35: Using pointers, write a program to set the elements in an array to zero.

#include <iostream>
#include <iterator>
using std::cout;
using std::endl;
using std::begin;
using std::end;

int main(int argc, char const *argv[])
{
	int ai[] = {1,3,5,7,9,11,13,15,17};
	for(auto pBeg = begin(ai); pBeg != end(ai); ++pBeg) {
		*(pBeg) = 0;
	}

	for (auto i : ai) {
		cout << i << " ";
	}
cout << endl;
	return 0;
}

ex.3.36
Exercise 3.36: Write a program to compare two arrays for equality. Write a similar program to compare two vectors.

CODE: Q.1 Q.2

ex.3.37
Exercise 3.37: What does the following program do?

const char ca[] = {'h', 'e', 'l', 'l', 'o'};
const char *cp = ca;
while (*cp) {
    cout << *cp << endl;
++cp;
}
The code is going to print out h e l l o line by line.

ca is a c style string. When initializing, C++ adds /0 automatically to the end of the string ca。Next, we defined a pointer that points to the first element of ca. In the loop, we printed out the element that was pointed by cp, then move cp to the next element. *cp will return true as long as it doesn't hit the end sign /0. when cp reaches the /0, *p is going to return 0 and cause the loop to end.

You may want to define as c style string with /0 to avoid potential problems:
const char ca[] = {'h', 'e', 'l', 'l', 'o',' /0'};

ex.3.38
Exercise 3.38: In this section, we noted that it was not only illegal but meaningless to try to add two pointers. Why would adding two pointers be meaningless?

The result of adding two addresses is absolutely meaningless. A pointer arithmetic operation is only meaningful when it has the meaning moving, which is restricted by two factors:

  • If the result is an integral type, then it must denote a “distance”.
  • If the result is a pointer, then the location pointed by the pointer must in the range.
ex.3.39
Exercise 3.39: Write a program to compare two strings. Now write a program to compare the values of two C-style character strings.

#include <iostream>
#include <string>
#include <cstring>
using std::cout;
using std::endl;
using std::string;

int main(int argc, char const *argv[])
{
	string s1 = "hello";
	string s2 = "hello";

	(s1 == s2) ? cout << "s1 Equal to s2. " : cout << "s1 is not Equal to s2. " ;
	cout << endl;

	const char cs1[] = {"Hello"};
	const char cs2[] = {"Healo"};

	(strcmp(cs1,cs2) == 0) ? cout << "cs1 Equal to cs2. " : cout << "cs1 is not Equal to cs2. " ;
	
	cout << endl;
	return 0;
}

ex.3.40
Exercise 3.40: Write a program to define two character arrays initialized from string literals. Now define a third character array to hold the concatenation of the two arrays. Use strcpy and strcat to copy the two arrays into the third.

#include <iostream>
#include <cstring>
using std::cout;
using std::endl;

int main(int argc, char const *argv[])
{
	char cs1[] = {"hello "};
	char cs2[] = {"world!"};
	char cs3[20] ={""};

	strcpy(cs3, cs1);
	strcat(cs3, cs2);

	cout << cs3 << endl; 
	return 0;
}

Ex.3.41-3.45

ex.3.41
Exercise 3.41: Write a program to initialize a vector from an array of ints.

#include <iostream>
#include <vector>
#include <cstring>

using std::vector;
using std::begin;
using std::end;
using std::cout;
using std::endl;

int main(int argc, char const *argv[])
{
	int ai[] = {1,2,3,4,5,6};
	vector<int> vi (begin(ai), end(ai));

	for(auto i : vi)
		cout << i << " ";
	cout << endl;
	return 0;
}

ex.3.42
Exercise 3.42: Write a program to copy a vector of ints into an array of ints.

CODE: Q.1

ex.3.43
Exercise 3.43: Write three different versions of a program to print the elements of ia. One version should use a range for to manage the iteration, the other two should use an ordinary for loop in one case using subscripts and in the other using pointers. In all three programs write all the types directly. That is, do not use a type alias, auto, or decltype to simplify the code.

CODE: Q.1

ex.3.44
Exercise 3.44: Rewrite the programs from the previous exercises using a type alias for the type of the loop control variables.

CODE: Q.1

ex.3.45
Exercise 3.45: Rewrite the programs again, this time using auto.

CODE: Q.1