What & How & Why

Chapter.6

第六章的习题答案


Ex.6.1-6.10

ex.6.1
Exercise 6.1: What is the difference between a parameter and an argument?

A parameter is a local variable, a part of a function. It receives an argument as an initializer. An argument is a value that is being supplied to initialize the parameter when calling a function.

ex.6.2
Exercise 6.2: Indicate which of the following functions are in error and why. Suggest how you might correct the problems.

(a) int f() {
string s; 
// ...
return s; //The return type of the function is int, but it returns a string value.
}
Fix:
string f() {
    string s; 
    // ...
    return s; 
}

(b) f2(int i) { /* ... */ } //The function has no return type.
Fix:
void f2 (int i)  { /* ... */ }

(c) int calc(int v1, int v1) /* ... */ } //Duplicate name in parameters.
Fix:
int calc(int v1, int v2) /* ... */ } 

(d) double square(double x) return x * x; //The function needs a pair of curly brace to claim its scope.
Fix:
double square(double x) {return x * x};

ex.6.3
Exercise 6.3: Write and test your own version of fact.

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

int fact(unsigned int n) {
	int ret{1};
	for (; n > 0;) {
		ret *= n--;
	}
	return ret;
}

int main(int argc, char const *argv[])
{
	unsigned int fact_num {5};
	cout << fact(fact_num) << endl;
	return 0;
}

ex.6.4
Exercise 6.4: Write a function that interacts with the user, asking for a number and generating the factorial of that number. Call this function from main.

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

int fact(unsigned int n) {
	int ret{1};
	for (; n > 0;) {
		ret *= n--;
	}
	return ret;
}


int main(int argc, char const *argv[])
{
	unsigned int fact_num {0};
	cin >> fact_num;
	cout << fact(fact_num) << endl;
	return 0;
}

ex.6.5
Exercise 6.5: Write a function to return the absolute value of its argument.

int my_abs (int i) {
	return i >= 0 ? i : -i;
}

ex.6.6
Exercise 6.6: Explain the differences between a parameter, a local variable, and a local static variable. Give an example of a function in which each might be useful.
  • Local Variable: A local variable is initialized by an argument or a initializer that is created inside a function. It will be destroyed when a function block ends.
  • Parameter: A parameter is a local variable that is initialized by an argument.
  • Local static Variable: A local static variable is defined inside the function, initialized when a function starts to run, and would last across multiple function calls, until the final call of the function.

int add_and_count (unsinged int num) { //num is a parameter.
	static unsinged int count {0}; //count is a local static variable that counts how many times the function runs.
	int i {1}; // i is a local variable for adding purpose.
	cout << "The " << num <<" + 1 = " << num + i << endl;
	cout << "The function runs " << count << " times." << endl;
}

int main(int argc, char const *argv[])
{
	add_and_count(1);
	add_and_count(2);
	add_and_count(3);
	return 0;
}

ex.6.7
Exercise 6.7: Write a function that returns 0 when it is first called and then generates numbers in sequence each time it is called again.

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

int count_ret () {
	static unsigned int count {0};
	return ++count;
}

int main(int argc, char const *argv[])
{
	for (int i = 0; i < 5; ++i)
		cout << count_ret() << endl;
	return 0;
}

ex.6.8
Exercise 6.8: Write a header file named Chapter6.h that contains declarations for the functions you wrote for the exercises in § 6.1 (p. 205).

#ifndef CHAPTER6_H
#define CHAPTER6_H
int fact(unsigned int n); //function in ex.6.3
int my_abs(int i); //function in ex.6.5
#endif

ex.6.9
Exercise 6.9: Write your own versions of the fact.cc and factMain.ccfiles. These files should include your Chapter6.h from the exercises in theprevious section. Use these files to understand how your compiler supportsseparate compilation.

CODE: fact.cc factMain.cc Chapter6.h

ex.6.10
Exercise 6.10: Using pointers, write a function to swap the values of two ints. Test the function by calling it and printing the swapped values.

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

void swap_int(int *pi, int *pj) {
	int temp = *pi;
	*pi = *pj;
	*pj = temp;
}

int main(int argc, char const *argv[])
{
	int i{0},j{0};
	cin >> i >> j;
	swap_int(&i, &j);
	cout << i << " " << j << endl;
	return 0;
}

Ex.6.11-6.20

ex.6.11
Exercise 6.11: Write and test your own version of reset that takes a reference.

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

void reset(int& ri) {
	ri = 0;
}

int main(int argc, char const *argv[])
{
	int i{0};

	while (cin >> i) {
		reset(i);
		cout << i << endl;
	}

	return 0;
}

ex.6.12
Exercise 6.12: Rewrite the program from exercise 6.10 in § 6.2.1 (p. 210) to use references instead of pointers to swap the value of two ints. Which version do you think would be easier to use and why?

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

void swap_intr (int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

int main(int argc, char const *argv[])
{
	int i{0},j{0};
	cin >> i >> j;
	swap_intr(i, j);
	cout << i << " " << j << endl;
	return 0;
}
In this case, the passing reference method is taking advantage. Since passing a pointer is actually copying the pointer where stored in argument to the parameter, the value which is pointed by the parameter may be changed through the pointer in argument potentially. The operation could be avoided by using the passed-by-reference method.

ex.6.13
Exercise 6.13: Assuming T is the name of a type, explain the difference between a function declared as void f(T) and void f(T&).
  • The first form: the parameter is a copy of the argument, changing the value of the parameter doesn't affect the argument.
  • The second form: while calling the function, a reference is bound to the argument and passed to the function. Any modification to the reference affects the argument.
ex.6.14
Exercise 6.14: Give an example of when a parameter should be a reference type. Give an example of when a parameter should not be a reference.

using reference when you intend to change a argument in the a function:

void reset(int& ri) {
	ri = 0;
}
using reference when the argument is large:
bool is_shorter(const string &s1, const string &s2) {
	return s1.size() < s2.size();
}
using reference when passing class, for example, iostream object:
void print(std::ostream &os) {/*.... */}

using passed-by-value method when you don't want the changing of the parameter affect the argument:

int fact(unsigned int n) {
	int ret{1};
	for (; n > 0;) {
		ret *= n--;
	}
	return ret;
}
using passed-by-value method when you have to use pointer:
void generateArray(int *a, int si)
{
    for (int j = 0; j < si; j++)
        a[j] = rand() % 9;
}

ex.6.15
Exercise 6.15: Explain the rationale for the type of each of find_char’s parameters In particular, why is s a reference to const but occurs is a plain reference? Why are these parameters references, but the char parameter c is not? What would happen if we made s a plain reference? What if we made occurs a reference to const?

Why is s a reference to const?

When passing a large object, using reference to avoid copies, which could save memory and time,is considered as a performance improvement. Since we don't want to change any thing of the string s, we applied the const keyword to ensure s couldn't be modified through the reference.

But occurs is a plain reference?

cocurs is an argument that is used to record how many times the letter appears. Since it needs to be updated inside of the function, we use plain reference to pass it.

Why are these parameters references, but the char parameter c is not?

In the program, char c is only used for a letter matching purpose. it is small so that we don't worry about the copying cost; Besides, it doesn't need to be changed; so using the passed-by-value method is good here.

What would happen if we made s a plain reference?

Since there is no modification related to string s in this program, making s a plain reference won't change the result. However, the potential modification error on s may occur since it is writeable now.

What if we made occurs a reference to const?

The program won't compile since we are trying to update a value through a reference-to-const.

Ex.6.16
Exercise 6.16: The following function, although legal, is less useful than it might be. Identify and correct the limitation on this function:

bool is_empty(string& s) { return s.empty(); }
The function is trying to check if a string is empty. In this case, it is not necessary to change the content of the string. A reference to a const string type parameter should be applied here.
bool is_empty(const string& s) { return s.empty(); }

Exercise 6.17: Write a function to determine whether a string contains any capital letters. Write a function to change a string to all lowercase. Do the parameters you used in these functions have the same type? If so, why? If not, why not?

No. the first function should have a read-only string parameter since the function does nothing to do with the string. The second function needs a plain reference to string type parameters because the string needs modification permission to perform lowercasing for the letters.

CODE: Q.1

ex.6.18
Write declarations for each of the following functions. When you write these declarations, use the name of the function to indicate what the function does.

* (a) A function named compare that returns a bool and has two parameters that are references to a class named matrix.

bool compare (const matrix& a, const matrix& b) {/* .. */};

  • (b) A function named change_val that returns a vector<int> iterator and takes two parameters: One is an int and the other is an iterator for a vector<int>.

vector<int>::itertor change_val (int i, vector<int>::itertor it) {}

ex.6.19
Exercise 6.19: Given the following declarations, determine which calls are legal and which are illegal. For those that are illegal, explain why.

double calc(double);
int count(const string &, char);
int sum(vector<int>::iterator, vector<int>::iterator, int);
vector<int> vec(10);
(a) calc(23.4, 55.1); //illegal. cale() only needs 1 argument
(b) count("abcda", 'a'); //legal
(c) calc(66); //legal
(d) sum(vec.begin(), vec.end(), 3.8); //legal

ex.6.20
Exercise 6.20: When should reference parameters be references to const? What happens if we make a parameter a plain reference when it could be a reference to const?

When should reference parameters be references to const?

  • if you don't want to change the parameter
  • if passing a large argument
  • if modifying of argument is demanded

What happens if we make a parameter a plain reference when it could be a reference to const?

  • The input range is narrowed: the function can't accept a low-level const parameter anymore.
  • Callers are misled: they thought they might modify the parameter, which is against your intention.

Ex.6.21-6.30

ex.6.21
Exercise 6.21: Write a function that takes an int and a pointer to an int and returns the larger of the int value or the value to which the pointer points. What type should you use for the pointer?

int int_comp(const int i, const int* const pi) {
	return i > *pi ? i : *pi;
}

ex.6.22
Exercise 6.22: Write a function to swap two int pointers.

void swap_ptr (const int* &pi, const int* &pj) {
	const int *temp = pi;
	pi = pj;
	pj = temp;
}

ex.6.23
Exercise 6.23: Write your own versions of each of the print functions presented in this section. Call each of these functions to print i and j defined as follows:

int i = 0, j[2] = {0, 1};
CODE: Q.1

ex.6.24
Exercise 6.24: Explain the behavior of the following function. If there are problems in the code, explain what they are and how you might fix them.

void print(const int ia[10])
{
	for (size_t i = 0; i != 10; ++i)
		cout << ia[i] << endl;
}
The function is working. However, it just looks like the function received the size of the array. The parameter only passed its name; The real thing that matters the loop is the variable i, which has nothing to do with the parameter const int ia[10]. The loop time is not guaranteed by the parameter, even though we changed the argument to an array with 20 elements, for instance, the function would run the loop for 10 times only.

The way to pass the size of an array to a function is by using the reference of the array. a size of an array is part of the definition of the reference of the array. When passing, the function checks if the argument size matches the size in the definition. Fix:

void print(const int (&ia) [10])
{
	for (size_t i = 0; i != 10; ++i)
		cout << ia[i] << endl;
}

ex.6.25
Exercise 6.25: Write a main function that takes two arguments. Concatenate the supplied arguments and print the resulting string.

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

int main(int argc, char const *argv[])
{
	string str {""};
	for(int i = 0; i != argc; ++i)
		str += argv[i];
	cout << "The concatenated string is: ";
	cout << str << endl;
	return 0;
}

ex.6.26
Exercise 6.26: Write a program that accepts the options presented in this section. Print the values of the arguments passed to main.

Please refer to ex.6.25 for the source code. CLI commands:

#command
root@Hare:/CppPrimer_5th/ch6# ./a.out -d -o ofile data0
#output
The concatenated string is: ./a.out-d-oofiledata0

ex.6.27
Exercise 6.27: Write a function that takes an initializer_list<int> and produces the sum of the elements in the list.

CODE: Q.1

ex.6.28
Exercise 6.28: In the second version of error_msg that has an ErrCode parameter, what is the type of elem in the for loop?

const string.

ex.6.29
Exercise 6.29: When you use an initializer_list in a range for would you ever use a reference as the loop control variable? If so, why? If not, why not?

Not for small-type elements. because elements of an initializer_list are all const variables. However, elements as large as a string should use a reference instead.

ex.6.30
Exercise 6.30: Compile the version of str_subrange as presented on page 223 to see what your compiler does with the indicated errors.

# g++

#error 1
root@Hare:/CppPrimer_5th/ch6# g++ test.cc
test.cc: In function ‘const string& ret_str()’:
test.cc:4:1: error: return-statement with no value, in function returning ‘const string& {aka const std::__cxx11::basic_string<char>&}’ [-fpermissive]
 return;
 ^~~~~~
#error 2
root@Hare:/CppPrimer_5th/ch6# g++ test.cc
test.cc: In function ‘const string& ret_str()’:
test.cc:4:14: warning: reference to local variable ‘s’ returned [-Wreturn-local-addr]
  std::string s = "Hello world";

Ex.6.31-6.40

ex.6.31
Exercise 6.31: When is it valid to return a reference? A reference to const?

When is it valid to return a reference?

As long as the reference is not refering to a local variable, it is valid to return it.

A reference to const?

Since a non-const object can initialize a low-const variable, return a vaild reference to const has the same requirement as the plain one.

ex.6.32
Exercise 6.32: Indicate whether the following function is legal. If so, explain what it does; if not, correct any errors and then explain it.

int &get(int *arry, int index) { return arry[index]; }
int main() {
	int ia[10];
	for (int i = 0; i != 10; ++i)
		get(ia, i) = i;
}
legal. The program is about write i to ia[i] 10 times. i will increase by 1 each step.

ex.6.33
Exercise 6.33: Write a recursive function to print the contents of a vector.

CODE: Q.1

ex.6.34
Exercise 6.34: What would happen if the stopping condition in factorial were if (val != 0)?

Assuming we don’t have negative input (because factorial is not available for negative numbers). The problem is when val == 0, the parameter starts to approach negative infinity. The stop condition will never be satisfied; Thus our recursive loop will run forever, and finally, the stack overflows.

PS: the $0!$ is actually $1$!

ex.6.35
Exercise 6.35: In the call to fact, why did we pass val - 1 rather than val–?

val– yields a val, not val - 1. There is no recursive happens because the function is always calling the original val.

ex.6.36
Exercise 6.36: Write the declaration for a function that returns a reference to an array of ten strings, without using either a trailing return, decltype, or a type alias.

std::string (&arrT())[10];

ex.6.37

Exercise 6.37: Write three additional declarations for the function in the previous exercise. One should use a type alias, one should use a trailing return, and the third should use decltype. Which form do you prefer and why?

//alias
using s_ten_r = string (&)[10];
s_ten_r arrT1();
//trail return type
auto arrT2() -> string (&)[10];
//decltype
string sa[10] = {"","","","","","","","","",""};
decltype(sa) &arrT3();
I preferred the trail return type form. it is more esaier to understand and write.

ex.6.38
Exercise 6.38: Revise the arrPtr function on to return a reference to the array.

decltype(odd)& arrPtr(int i) {
	return (i % 2) ? &odd : &even; 
}

ex.6.39

Exercise 6.39: Explain the effect of the second declaration in each one of the following sets of declarations. Indicate which, if any, are illegal.

//They are the same function since the top-const is ignored when initializing. Can't complie.
(a) int calc(int, int);
int calc(const int, const int);

//They are the same function. The return type of the function doesn't matter overloading. Can't complie.
(b) int get();
double get();

//They are different functions because they have different types of parameters. Can complie.
(c) int *reset(int *);
double *reset(double *);

ex.6.40
Exercise 6.40: Which, if either, of the following declarations are errors? Why?

(a) int ff(int a, int b = 0, int c = 0); //legal
(b) char *init(int ht = 24, int wd, char bckgrnd);//illegal. default arguments should start from the right-most.

Ex.6.41-6.50

ex.6.41
Exercise 6.41: Which, if any, of the following calls are illegal? Why? Which,if any, are legal but unlikely to match the programmer’s intent? Why?

char *init(int ht, int wd = 80, char bckgrnd = ' ');
(a) init(); //illegal, ht has no default arugment.
(b) init(24,10); //legal
(c) init(14, '*'); //legal, but '*' will be assgined to wd and converted to an int.

ex.6.42
Exercise 6.42: Give the second parameter of make_plural (§ 6.3.2, p.224) a default argument of 's'. Test your program by printing singular and plural versions of the words success and failure.

CODE: Q.1

ex.6.43
Exercise 6.43: Which one of the following declarations and definitions would you put in a header? In a source file? Explain why.

(a) inline bool eq(const BigInt&, const BigInt&) {...}
Inline functions may be defined more than once, as they are inline functions. Therefore, this function should be in a header. Including an inline function in a header makes sharing it easier.

(b) void putValues(int *arr, int size);
The function should also be put in a header since it does not come with a definition. A common way to share a function is to define it in a source file (the header file that declares the function may also be included in the source file), declare it in a header file, and then use it by including the header.

ex.6.44
Exercise 6.44: Rewrite the isShorter function from § 6.2.2 (p. 211) to be inline.

inline bool 
isShorter(const string &s1, const string &s2) {
	return s1.size() < s2.size();
}

ex.6.45
Exercise 6.45: Review the programs you’ve written for the earlier exercises and decide whether they should be defined as inline. If so, do so. If not, explain why they should not be inline.

some functions that should be writed as an inline function:

//get the absolute value of an int 
int my_abs (int i) { return i >= 0 ? i : -i; };
//compare two ints an return the large one
int int_comp(const int i, const int* const pi) { return i > *pi ? i : *pi; }
//adding plural ending
string make_plural (size_t ctr, cstr word, cstr ending = "s") {
	return (ctr > 1) ? word + ending : word;
}
All of the functions above are written in a line and might be called multiple times. Therefore they should be written as inline functions. Some of them should not, however:
//print an vector recursively
void print_vec(const it pbeg, const it pend) {
	
	if (pend != pbeg + 1)
		print_vec(pbeg , pend - 1);	
	cout << *(pend - 1) << " ";
}
It is not ideal to inline functions that have multiple lines or complicated logic.

ex.6.46
Exercise 6.46: Would it be possible to define isShorter as a constexpr? If so, do so. If not, explain why not.

No. A constexpr function should have no rum-time actions, however, isShorter function calls std::string.size() function, which is not a const expression.

ex.6.47
Exercise 6.47: Revise the program you wrote in the exercises in § 6.3.2 (p. 228) that used recursion to print the contents of a vector to conditionally print information about its execution. For example, you might print the size of the vector on each call. Compile and run the program with debugging turned on and again with it turned off.

When NDEBUG is ON:

root@Hare:/CppPrimer_5th/ch6# ./a.out
1 2 3 4 5 6 7 8 9 10
When NDEBUG is OFF:
root@Hare:/CppPrimer_5th/ch6# ./a.out
a.out: ex_6_47.cc:26: void vec_rec_print(vecit, vecit): Assertion `pend == pbeg' failed.
CODE: Q.1

ex.6.48
Exercise 6.48: Explain what this loop does and whether it is a good use ofassert:

string s;
while (cin >> s && s != sought) { } // empty body
    assert(cin);
It is not a good use of assert. The loop continues to get input from cin, and it stops when sought get matched. The problem in this example is that assert is not correctly placed. 'assert' does nothing when the condition is true. The condition is false in two situations: EOF or s matches sought, however, regardless of the situation, assert evaluates the cin with EOF value. Due to this, assert(cin) always returns “false”, which is useless for debugging in this example.

ex.6.49
Exercise 6.49: What is a candidate function? What is a viable function?

What is a candidate function?

Set of functions that are considered when resolving a function call. (all the functions with the name used in the call for which a declaration is in scope at the time of the call.)

What is a viable function?

Subset of the candidate functions that could match a given call. It have the same number of parameters as arguments to the call, and each argument type can be converted to the corresponding parameter type.
ex.6.50
Exercise 6.50: Given the declarations for f from page 242, list the viable functions, if any for each of the following calls. Indicate which function is the best match, or if the call is illegal whether there is no match or why the call is ambiguous.

//a The call is an ambiguous call 
f(2.56, 42);

//b
void f(int); //best match function
f(42)

//c
void f(int, int); //best match function
f(42, 0)

//d
void f(double, double = 3.14); //best match function
f(2.56, 3.14)

Ex.6.51-6.56

ex.6.51
Exercise 6.51: Write all four versions of f. Each function should print a distinguishing message. Check your answers for the previous exercise. If your answers were incorrect, study this section until you understand why your answers were wrong.

CODE: Q.1

ex.6.52
Exercise 6.52: Given the following declarations, what is the rank (§ 6.6.1, p. 245) of each conversion in the following calls?

void manip(int, int);
double dobj;
(a) manip('a', 'z'); //rank 3, intergal promotion
(b) manip(55.4, dobj);// rank 4, arithmetic conversion

ex.6.53
Exercise 6.53: Explain the effect of the second declaration in each one of the following sets of declarations. Indicate which, if any, are illegal.

(a) int calc(int&, int&);
int calc(const int&, const int&); // a new function, takes reference to const

(b) int calc(char*, char*);
int calc(const char*, const char*); // a new function, takes pointer to const

(c) int calc(char*, char*); // take char*
int calc(char* const, char* const); // illegal, the same as the frist funcion, will cause ambiguous call

ex.6.54
Exercise 6.54: Write a declaration for a function that takes two int parameters and returns an int, and declare a vector whose elements have this function pointer type.

int func(int, int); //a function
int (*) (int, int); //the type of a poiner to the function
std::vector<int (*) (int, int)> vpif(); //a vector contains pointers with the type above

ex.6.55
Exercise 6.55: Write four functions that add, subtract, multiply, and divide two int values. Store pointers to these values in your vector from the previous exercise.

CODE: Q.1

ex.6.56
Exercise 6.56: Call each element in the vector and print their result.

See code in 6.55