Exercise 6.1

1
2
3
// parameters of function: local variables are to be implicitly defined and initialized when the function is called.
// arguments of function: initialize values that assigned to objects(paramenters of a function) when a function is called

Exercise 6.2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// a: type of function unmatchs its return value
// string f()
// {
//   string s;
//   // ...
//   return s;
// }
// b: type of return value not specified
// int f2(int i) { /* ... */ }
// c: missing left '{', parameters with same name
// int calc(int v1, int v2) { /* ... */ }
// d: missing a pair of curly braces(the function body must be a block)
// double square(double x) {return x * x;}

Exercise 6.3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
unsigned fract(unsigned x)
{
  unsigned rel = 1;
  while(x > 0)
    rel *= x--;
  return rel;
}
int main()
{
  cout << "Enter a integer above 0 to competer its fraction: ";
  unsigned i;
  cin >> i;
  unsigned result = fract(i);
  cout << "The fraction of " << i << " is " << result << endl;
}

Exercise 6.4

1
2
// Oh I implement it in exercise 6.3

Exercise 6.5

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
unsigned abs_value(int i)
{
  return i >=0 ? i : -i;
}
int main()
{
  cout << "Please a integer to compute its abs value: ";
  int ival;
  cin >> ival;
  unsigned result = abs_value(ival);
  cout << "The abs value of " << ival << " is " << result << endl;
}

Exercise 6.6

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// parameter and local variable are both automatic object, while parameter is defined at the beginning of function calling process, local variable are defined later, which is obvious that parameter list is placed behind the function name, local variable is located in the function body
// local static object is defined inside function body but it would not be perished even the function ends, it would be so only when the whole program terminates; the definition statement of local static object would be skipped when this certain function be recalled.
unsigned abs_value(int i)
{
  unsigned result;
  result = (i >= 0) ? i : -i;
  cout << "The abs value of " << i << " is: " << result << endl;
  static unsigned count = 0;
  return ++count;
}

Exercise 6.7

1
2
3
4
5
6
int main()
{
  static int count = -1;
  return ++count;
}

Exercise 6.10

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void exchange(int *p1, int *p2)
{
  int temp;
  temp = *p1;
  *p1 = *p2;
  *p2 = temp;
}
int main()
{
  cout << "Please enter integer numbers: ";
  int i1, i2;
  cin >> i1 >> i2;
  cout << "first int num: " << i1
       << " second int num: " << i2 << endl;
  exchange(&i1, &i2);
  cout << "after exchanging first int num: " << i1
       << " second int num: " << i2 << endl;
}

Exercise 6.11

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void reset(int &r)
{
  r = 0;
}
int main()
{
  cout << "Enter a int num: ";
  int i;
  cin >> i;
  cout << "Before reset the value is: " << i << endl;
  reset(i);
  cout << "After reset the value is: " << i << endl;
}

Exercise 6.12

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void exchange(int &p1, int &p2)	// of course called by reference is better
{
  int temp;
  temp = p1;
  p1 = p2;
  p2 = temp;
}
int main()
{
  cout << "Please enter integer numbers: ";
  int i1, i2;
  cin >> i1 >> i2;
  cout << "first int num: " << i1
       << " second int num: " << i2 << endl;
  exchange(i1, i2);		// just pass the variable, not a &i1...
  cout << "after exchanging first int num: " << i1
       << " second int num: " << i2 << endl;
}

Exercise 6.13

1
2
3
// void f(T) initialize the parameter with value of type T argument, the value is copied to the parameter
// void f(&T) initialize the parameter as a reference to the passed type T argument.

Exercise 6.14

1
2
3
4
5
6
7
8
9
int called_cnt(int &cnt)	// calculate the times a function is called
{
  return ++cnt;
}
unsigned abs_value(int i)	// calculate the abs value of one integer, the original argument maybe required in the later context
{
  return i >= 0 ? i : -i;
}

Exercise 6.15

1
2
3
4
5
// string::size_type find_char(const string &s, char c, string::size_type &occurs);
// there is no need to modify the original argument, and the original string object argument maybe too long that the expense of copy is too high, so the first parameter is a reference to a const string; if it is a reference to a string, actions inside function body may modify the original string object and the compiler would allow it.
// copy of char type object costs a little, it could be a reference, but that would be unnecessary.
// in the contrast to the first parameter, there is a need to modify the third passed argument which is a string::size_type object, the third parameter should be a reference or a pointer; if it is a reference to a constant, the modifiment is impossibile and the compiler would raise an error.

Exercise 6.16

1
2
3
// bool is_empty(const string& s) {return s.empty()}
// the original one could not accept argument of const type, literal type and type which nees convertion

Exercise 6.17

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bool any_uppercase(const string& s)
{
  bool upper = false;
  for(char c : s)
    if(isupper(c))
      {
      upper = true;
      break;
      }
  return upper;
}
void lower_string(string& s)
{
  for(char& c : s)
    c = tolower(c);
}
int main()
{
  cout << "Enter a string: ";
  string s;
  cin >> s;
  bool any_upper = any_uppercase(s);
  lower_string(s);
  cout << "The input string " << any_upper << " uppercase "
       << "after transform " << s << endl;
}

Exercise 6.18

1
2
3
// a: bool compare(matrix &m1, matrix &m2); compare two matrix object, return true if the same, else false
// b: vector<int>::iterator change_val(int i; vector<int>::iterator iter); change the element pointed by iter which is the second parameter, change the value of this element to i which is the first paremeter, return the second parameter.

Exercise 6.19

1
2
3
4
5
6
7
8
9
// double calc(duble);
// 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, more arguments than parameters
// b: count("abcda", 'a'); legal
// c: calc(66); legal, 66 would be implicitly converted to double type
// d: sum(vec.begin(), vec.end(), 3.8); legal, 3.8 would be implicitly cinverted to int type

Exercise 6.20

1
2
3
// reference type parameter of function should be a reference to constant when there is no need to modify the original passed argument.
// situation that a parameter of function should be a reference to constant, but due to mistakes that it is settled as a reference, that parameter could not accept constant variable, literal type or certain type which needs convertion, what's worse, the function body may modify the original variable which is referenced to and the compiler would allow it.

Exercise 6.21

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int compare(const int i, const int *p)
{
  return i >= *p ? i : *p;
}
int main()
{
  cout << "Please enter two integers: ";
  int i1, i2;
  cin >> i1 >> i2;
  int bigger_one = compare(i1, &i2); // the second parameter should be int* type
  cout << "The bigger one is " << bigger_one << endl;
}

Exercise 6.22

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void exchange(int *p1, int *p2)
{
  int temp = *p1;
  *p1 = *p2;
  *p2 = temp;
}
int main()
{
  cout << "Please enter two integers: ";
  int i1, i2;
  cin >> i1 >> i2;
  exchange(&i1, &i2);
  cout << "After exchange the first is " << i1 << " the second is " << i2 << endl;
}

Exercise 6.23

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void print(const int ia[], size_t size)
{
  for(size_t i = 0; i != size; ++i)
    cout << ia[i] << endl;
}
void print(const int (&array)[2])
{
  for(int elem : array)
    cout << elem << endl;
}
int main()
{
  int i = 0, j[2] = {0, 1};
  print(&i, 1);
  print(j);
}

Exercise 6.24

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void print(const int ia[10])	// the real type of parameter is const int*
{
  for(size_t i = 0; i != 10; ++i) // it is dangerous, the array argument' length may be shorter than 10, but compiler would allow it, that results in an undefined behaviour
    cout << ia[i] << endl;
}
// correct one
void print(const int (&ia)[10])
{
  for(size_t i = 0; i != 10; ++i)
    cout << ia[i] << endl;
}

Exercise 6.25

1
2
3
4
5
6
7
8
int main(int argc, char **argv)
{
  if(argc != 3) return -1;
  string s1 = argv[1];
  string s2 = argv[2];
  cout << s1 + s2 << endl;
}

Exercise 6.26

1
2
3
4
5
6
int main(int argc, char **argv)
{
  for(size_t index = 1; argv[index] != 0; ++index)
    cout << argv[index] << endl;
}

Exercise 6.27

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int sum(initializer_list<int> il)
{
  int result = 0;
  for(const auto &i : il)
    result += i;
  return result;
}
int main()
{
  cout << sum({1, 2, 3, 4, 5}) << endl;
}

Exercise 6.28

1
2
// elem: const std::string&

Exercise 6.29

1
2
3
// could not change the type of loop variable to be reference, the variable'type should be reference to constant.
// as element in initializer_list object is always constant, it is forbidden to modify such element through reference, so type reference to constant is right.

Exercise 6.30

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
bool str_subrange(const string &str1, const string &str2)
{
  if(str1.size() == str2.size())
    return str1 == str2;
  auto size = (str1.size() < str2.size())
    ? str1.size() : str2.size();
  for(decltype(size) i = 0; i != size; ++i)
    {
      if(str1[i] != str2[i])
      return;
    }
}

Exercise 6.31

1
2
3
// the return reference will be invalid if it is binded to a local object of a function body
// same reason as before

Exercise 6.32

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int &get(int *arry, int index)
{
  // legal, for that this function's return type is int&, which is reference to element in arry
  return arry[index];		// [] operator's evaluated result is a left value
}
int main()
{
  int ia[10];
  for(int i = 0; i != 10; ++i)
    {
      get(ia, i) = i;
      cout << ia[i] << endl;
    }
}

Exercise 6.33

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void print_vector(vector<int>::iterator biter, vector<int>::iterator eiter)
{
  if(biter != eiter)
    {
      cout << *biter << endl;
      print_vector(++biter, eiter);
    }
}
int main()
{
  vector<int> iv = {1, 2, 3, 4, 5};
  print_vector(iv.begin(), iv.end());
}

Exercise 6.34

1
2
3
// the result remains the same if the argument is non-negative;
// else this function processing never stops and exceeds the maximum stack depth

Exercise 6.35

1
2
3
// val-- keeps a copy of the original value of val, decreases val by 1, and then return the kept copy.
// this function processing would never meet the end condition statement, exceeds the maximum stack depth.

Exercise 6.36

1
2
string (&example())[10];

Exercise 6.37

1
2
3
4
5
6
using stringArray = string[10];
stringArray& func1();
auto func2() -> string(&)[10];	// it is better, easy to use and understand
string array[10];
decltype(array) &func3();

Exercise 6.38

1
2
3
4
5
6
7
int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};
decltype(odd) &arrptr(int i)
{
  return (i % 2) ? odd : even;
}

Exercise 6.39

1
2
3
4
5
6
7
// a: int calc(int, int);
// int calc(const int, const int); legal overload that this function name could be called with two const int type arguments
// b: int get();
// double get(); illegal, exactly the same as the first function except for the return type, it is forbidden in C++
// c: int *reset(int *);
// double *reset(doouble *); legal, overload that this function name could be called with one double* argument and returns a double* value.

Exercise 6.40

1
2
3
// a: int ff(int a, int b = 0, int c = 0); legal
// b: char *init(int ht = 24, int wd, char bckground); illegal, it one parameter has default argument, the successive parameters must have default arguments, too.

Exercise 6.41

1
2
3
4
5
// char *init(int ht, int wd = 80, char bckground = ' ');
// a: init(); illegal, the first parameter does not have a default argment, so when init is called a related argument must be placed to initialize this parameter.
// b: init(24, 10); legal.
// c: init(14, '*'); legal but not properly called, though the second argument as a char object could be implicitly converted to int type, this calling would run successfully, but it is against the original design purpose.

Exercise 6.42

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
string make_plural(size_t ctr, const string &word, const string &ending = "s")
{
  return (ctr > 1) ? word + ending : word;
}
int main()
{
  string s1 = "success";
  string s2 = "failure";
  cout << make_plural(1, s1) << endl;
  cout << make_plural(1, s2) << endl;
  cout << make_plural(2, s1, "es") << endl;
  cout << make_plural(2, s2) << endl;
}

Exercise 6.43

1
2
3
// a: header file
// b: source file(not so strictly requires)

Exercise 6.44

1
2
3
4
5
inline bool isShorter(const string &s1, const string &s2)
{
  return s1.size() < s2.size();
}

Exercise 6.45

1
2
3
4
// there're too many fundtions...
// generally, recursive fundtions or complicated functions could not be enhanced with inline mechanism
// functions which are usually short, direct and frequently called could be transformed to inline functions.

Exercise 6.46

1
2
3
4
5
6
// it is possibile
constexpr bool isShorter(const string &s1, const string &s2)
{
  return s1.size() < s2.size() ? true : false;
}

Exercise 6.47

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void print_vector(vector<int>::iterator biter, vector<int>::iterator eiter)
{
  if(biter != eiter)
    {
      #ifndef NDEBUG		// g++ -D NDEBUG 6.47.c would define NDEBUG and that when processing next statement would be skipped
      cerr << "The size of vector: " << eiter - biter << endl;
      #endif
      cout << *biter << endl;
      print_vector(++biter, eiter);
    }
}
int main()
{
  vector<int> iv = {1, 2, 3, 4, 5};
  print_vector(iv.begin(), iv.end());
}

Exercise 6.48

1
2
3
4
// sring s;
// while(cin >> s && s != sought) { }
// assert(cin); not reasonable; cin object converted to bool which maybe true, for that in while loop the second condition is false but the first is true. If some one wants to get notified and the program automatically stopped when at last comes to that assert statement, that may be not come true.

Exercise 6.49

1
2
3
// a candidate function is a function in a set of overload functions, candidate function has the same name as the function which is called, and its declaration is available at that calling point.
// based on the arguments of function calling, if number of parameters of candidate function equals the number of arguments , and type of every argument matchs the type of every parameter or type convertion is possible, that candidate functon is a viable function.

Exercise 6.50

1
2
3
4
5
// a: f(2.56, 42); illegal, ambiguous call;
// b: f(42); legal, void f(int);
// c: f(42, 0); legal, void f(int, int);
// d: f(2.56, 3.14); legal, void f(double, double = 3.14);

Exercise 6.51

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void f()
{
  cout << 1 << endl;
}
void f(int)
{
  cout << 2 << endl;
}
void f(int, int)
{
  cout << 3 << endl;
}
void f(double, double = 3.14)
{
  cout << 4 << endl;
}
int main()
{
  // f(2.56, 42); ambiguous call
  f(42);
  f(42, 0);
  f(2.56, 3.14);
}

Exercise 6.52

1
2
3
4
5
void manip(int, int);
double dobj;
// a: manip('a', 'a'); 3
// b: manip(55.4, dobj); 4

Exercise 6.53

1
2
3
4
5
6
7
int calc(int&, int&);
int calc(const int&, const int&); // accepts constant int arguments, while called with non constant int arguments the first one would be choosed.
int calc(char*, char*);
int calc(const char*, const char*); // pattern is same as the previous one, while the type is const char* and char* now.
int calc(char*, char*);
int calc(char* const, char* const); // illegal, would results in ambiguous call, for that both functions would be precisely match when call calc(&s1, &s2) no matter s1 or s2 is const pointer or not.

Exercise 6.54

1
2
3
int func1(int, int);
vector<int(*)(int, int)> fv;

Exercise 6.55

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int func1(int a, int b)
{
  return a + b;
}
int func2(int a, int b)
{
  return a - b;
}
int func3(int a, int b)
{
  return a * b;
}
int func4(int a, int b)
{
  return a / b;
}
vector<int(*)(int, int)> fv{func1, func2, func3, func4};

Exercise 6.56

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int func1(int a, int b)
{
  return a + b;
}
int func2(int a, int b)
{
  return a - b;
}
int func3(int a, int b)
{
  return a * b;
}
int func4(int a, int b)
{
  return a / b;
}
vector<int(*)(int, int)> fv{func1, func2, func3, func4};
int main()
{
  for(const auto &f : fv)
    cout << f(2, 1) << endl;
}