What & How & Why

Chapter.4

第四章的习题答案


Ex.4.1-4.10

ex.4.1
Exercise 4.1: What is the value returned by 5 + 10 * 20/2?
  1. 10 * 20 = 200
  2. 200 / 2 - 100
  3. 5 + 100 = 105
ex.4.2
Exercise 4.2: Using Table 4.12 (p. 166), parenthesize the following expressions to indicate the order in which the operands are grouped:

(a) * vec.begin(); //  *(vec.(begin.()))
(b) * vec.begin() + 1; // (*(vec.(begin.()))) + 1

ex.4.3
Exercise 4.3: Order of evaluation for most of the binary operators is left undefined to give the compiler opportunities for optimization. This strategy presents a trade-off between efficient code generation and potential pitfalls in the use of the language by the programmer. Do you consider that an acceptable trade-off? Why or why not?

Yep. Considering if we bind the order of evaluation to precedence and associativity in the previous example:

f() + g() * h() + j()
what happens is g() and h() is about to be evaluated first, then the f() and j(). As long as the expressions do not affect the same object, it is not necessary to run the functions in order. We may evaluate them at the same time to get better performance.

I consider the trade-off is one exampe to demonstrate the philosophy of CPP, that is, let the programmer make the decision.

ex.4.4
Parenthesize the following expression to show how it is evaluated. Test your answer by compiling the expression (without parentheses) and printing its result.

((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2)
Result: 91

ex.4.5
Exercise 4.5: Determine the result of the following expressions.

(a) -30 * 3 + 21 / 5 // -90 + 4 = -86
(b) -30 + 3 * 21 / 5 // -30 + 12 = -18
(c) 30 / 3 * 21 % 5 // 10 * 21 % 5 = 0
(d) -30 / 3 * 21 % 4// -210 % 4 = -2

ex.4.6

Exercise 4.6: Write an expression to determine whether an int value is even or odd.

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

int main(int argc, char const *argv[])
{
	int i = 0;
	cin >> i;
	(i % 2 == 0) ? cout << "Even." : cout << "Odd.";
	cout << endl;
	return 0;
}

ex.4.7
Exercise 4.7: What does overflow mean? Show three expressions that will overflow.

Overflow means value computed is outside the range values that the type can repersent.

short sv = 32767; ++sv;
unsigned int ui = 0; --ui;
int i = 0; i = 2,147,483,648;

ex.4.8
Exercise 4.8: Explain when operands are evaluated in the logical AND, logical OR, and equality operators.

For && and ||, they obeys Short-Cut evaluation,which is going to evaluate the left operand first. IF the left operand couldn't give a result, then the right operand is about to be evaluated.

For ==,it doesn't guarantee which operands will be evaluated first. It is true when both operands produce the same result.

ex.4.9
Exercise 4.9: Explain the behavior of the condition in the following if:

const char *cp = "Hello World";
if (cp && *cp)
The left operand is cp, a pointer to the first element of char string cp. As long as the pointer is not null pointer, the condition is true.

The right operand is *cp,a char H. The condition with this operand will remain true as long as the char is not \0

In sum, the condition will return true.

ex.4.10
Exercise 4.10: Write the condition for a while loop that would read ints from the standard input and stop when the value read is equal to 42.

int i = 0;
while (cin >> i && i != 42) {...}

Ex.4.11-4.20

ex.4.11
Exercise 4.11: Write an expression that tests four values, a, b, c, and d, and ensures that a is greater than b, which is greater than c, which is greater than d.

a > b && b > c && c >> d

ex.4.12
Exercise 4.12: Assuming i, j, and k are all ints, explain what i != j < k means.

i != j < k equal to i != (j < k) . Since j < k is about to return a bool type, when comparing bool literial with int, bool will be convert into int. Thus, depending on whether j or k is greater, The final comparsion will be like : i != 0 or i != 1.

ex.4.13
Exercise 4.13: What are the values of i and d after each assignment?

int i; double d;
(a) d = i = 3.5; // d = (i = 3.5) -> i = 3 -> d = 3.0
(b) i = d = 3.5; // i = (d = 3.5) -> d = 3.5 -> i = 3

ex.4.14

Exercise 4.14: Explain what happens in each of the if tests:

if (42 = i) // error. assignment operator requires left operand must be a modifiable lvalue.
if (i = 42) // equal to if(42), return true

ex.4.15
Exercise 4.15: The following assignment is illegal. Why? How would you correct it?

double dval; int ival; int *pi;
dval = ival = pi = 0; //illegal. ival is an int that can't be assigned with a pointer value pi.
/* correction */
dval = ival = *pi = 0;

ex.4.16
Exercise 4.16: Although the following are legal, they probably do not behave as the programmer expects. Why? Rewrite the expressions as you think they should be.

(a) if (p = getPtr() != 0) // due to the low precedence, it would be better that assignment comes with a pair of parenthesis.
fix: if ((p =getPtr()) != 0);
/* ---------- */
(b) if (i = 1024) // the condition is forever true so it is useless here. May be the author just want to comparing i with 1024? then the fix:
if (i == 1024)

ex.4.17
Exercise 4.17: Explain the difference between prefix and postfix increment.

// prefix
int& int::operator++() // returne a reference, lvalue, 2 steps operation
{
  *this += 1;  // i = i + 1
  return *this;  // return an updated i
}
//postfix
const int int::operator++(int) //return int, rvalue, 3 steps operation
{
  int oldValue = *this;  // save i to i_temp
  ++(*this);  // i = i + 1
  return oldValue;  // return i_temp, a copy of original i
}

ex.4.18
Exercise 4.18: What would happen if the while loop on page 148 that prints the elements from a vector used the prefix increment operator?

hile (pbeg != v.end() && *beg >= 0)
cout << *pbeg++ << endl;
Since the ++beg returns beg+1, so the operation here is prints out the next valid element on current loop.

It can be dangerous because the iterator may access the off-the-end element when the loop is just finished.

ex.4.19
Exercise 4.19: Given that ptr points to an int, that vec is a vector<int>, and that ival is an int, explain the behavior of each of these expressions. Which, if any, are likely to be incorrect? Why? How might each be corrected?

(a) ptr != 0 && *ptr++ // correct, check if ptr is a nullptr, then read element through ptr, then to the next element
(b) ival++ && ival // correct. check if ival == 0, if not, then check if(ival + 1) == 0
(c) vec[ival++] <= vec[ival] // incorrect, op <= can't guarantee the order of evaluation.
vec[ival] <= vec [ival + 1] // if left evaluation happens first
vec[val] <= vec[ival] // if right evaluation happens first

ex.4.20
Exercise 4.20: Assuming that iter is a vector<string>::iterator, indicate which, if any, of the following expressions are legal. Explain the behavior of the legal expressions and why those that aren’t legal are in error.

(a) *iter++; //legal. read current element and move the iterator to the next position
(b) (*iter)++;//illegal. string has no self increment operation.
(c) *iter.empty() //illegal. iterator iter doesn't have member function empty().
(d) iter->empty(); //legal, indicates if the pointed string is empty.
(e) ++*iter;//illegal. deference iter first yields a string and string has no self increment operation.
(f) iter++->empty();//legalThe expression is will do dereference *(iter) first, then access call the member function *(iter).emtpy(), then do the self increment.

Ex.4.21-4.30

ex.4.21
Exercise 4.21: Write a program to use a conditional operator to find the elements in a vector<int> that have odd value and double the value of each such element.

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

int main(int argc, char const *argv[])
{
	vector<int> vi {1,2,3,4,5,6,7,8,0,10,11};
	for (auto &i : vi) {
		(i & 0x1) ? (i *= 2) : i;
	}

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

ex.4.22
Exercise 4.22: Extend the program that assigned high pass, pass, and fail grades to also assign low pass for grades between 60 and 75 inclusive. Write two versions: One version that uses only conditional operators; the other should use one or more if statements. Which version do you think is easier to understand and why?

CODE: Q.1

I would go with the if version. Nesting more than 3 condtionals is extreamely harder to read.

ex.4.23
Exercise 4.23: The following expression fails to compile due to operator precedence. Using Table 4.12 (p. 166), explain why it fails. How would you fix it?

string s = "word";
string pl = s + s[s.size() - 1] == 's' ? "" : "s" ;
The second line is actually evaluated in the following order:
1. s[s.size() - 1]
2. s + s[s.sise() -1]
3. (s + s[s.size() - 1])  == 's' //error here, a string is not comparable with a character.
4. the bool result from 3 ? "" : "s"
In my opinion, the orginal logic is :

  1. check whether the last letter of the string s is letter 's'
  2. if it is, add nothing to the end of the string s
  3. otherwise, add letter s to string s

Fix:

string pl = s + ((s[s.size() - 1] == 's') ? "" : "s" );
PS. a test of comparing char and stl string, and the result:
char a = 's';
std::string s = "s";
std::cout << (s == a) << std::endl;
return 0;

test.cc:14:18: error: no match for ‘operator==’ (operand types are ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ and ‘char’)
std::cout << (s == a) << std::endl;

ex.4.24
Exercise 4.24: Our program that distinguished between high pass, pass, and fail depended on the fact that the conditional operator is right associative. Describe how that operator would be evaluated if the operator were left associative.

(grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";
wll be like
((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";
if it is left-associative. The whole process is:

  1. if grade > 90, then the result is “high pass”, otherwise a bool (the result of grade < 60)
  2. depending on the result, the condition for the next operation would be a string or bool.

It is clear that the whole logic is messed up at the second step. Supposing the score is greater than 90, then the result is “high pass” after the first comparison. A non-empty string will always be true in condition. Thus, as long as your score is greater than 90, you will get a fail grade.

ex.4.25
Exercise 4.25: What is the value of ~'q' « 6 on a machine with 32-bit ints and 8 bit chars, that uses Latin-1 character set in which 'q' has the bit pattern 01110001?

The reasult is undefined.

When doing ~q, 'q' was promoted to the int type first. The size of q changed from 8 to 32:

01110001 //char
00000000 00000000 00000000 01110001 //int
Then the inverse appiled:
00000000 00000000 00000000 01110001 //int
11111111 11111111 11111111 10001110 //result of ~q
The result of ~q is negative. Thus, doing ~q « 6 shfits a negative value to left, which is undefined.

ex.4.26
Exercise 4.26: In our grading example in this section, what would happen if we used unsigned int as the type for quiz1?

In the example, we need to compare the value at the 27th bit. However, an unsigned int only has 16 bits, which is not big enough to hold the information. Moreover, a shifting that causes an out-of-range result is undefined.

ex.4.27
What is the result of each of these expressions?

unsigned long ul1 = 3, ul2 = 7;
(a) ul1 & ul2 //3
(b) ul1 | ul2 //7
(c) ul1 && ul2 //true
(d) ul1 || ul2 //true

ex.4.28
Exercise 4.28: Write a program to print the size of each of the built-in types.

CODE: Q.1

ex.4.29
Exercise 4.29: Predict the output of the following code and explain your reasoning. Now run the program. Is the output what you expected? If not, figure out why.

int x[10]; int *p = x;
cout << sizeof(x) / sizeof(*x) << endl; //1
cout << sizeof(p) / sizeof(*p) << endl; //
The result of the first one is 10. It actually returns the number of the elements in x by using the size of x to divide the size of the element.

The result of the sec one on my PC is 2. A pointer takes 8 bytes of space, an int takes 4 bytes on my PC.

ex.4.30
Exercise 4.30: Using Table 4.12 (p. 166), parenthesize the following expressions to match the default evaluation:

(a) sizeof x + y // (sizeof x) + y
(b) sizeof p->mem[i] //sizeof (p->mem[i])
(c) sizeof a < b // (sizeof a) < b
(d) sizeof f() // sizeof (f())

Ex.4.31-4.38

ex.4.31
Exercise 4.31: The program in this section used the prefix increment and decrement operators. Explain why we used prefix and not postfix. What changes would have to be made to use the postfix versions? Rewrite the program using postfix operators.

We have already discussed the difference between prefix in/decrement and postfix in/decrement in the previous section. For this section, the only thing that is affected by switching to the postfix version is efficiency. The program would run much slower If the computation were massive.

ex.4.32
Exercise 4.32: Explain the following loop.

constexpr int size = 5;
int ia[size] = {1,2,3,4,5};
for (int *ptr = ia, ix = 0;
	 ix != size && ptr != ia+size;
	 ++ix, ++ptr) { 
	 /* ... */ 
}
The for loop will traverse the whole array. The pointer ptr points to the current element in loop and the ix seems to be the index of the current element.

ex.4.33
Exercise 4.33: Using Table 4.12 (p. 166) explain what the following expression does:

someValue ? ++x, ++y : --x, --y
Since comma operator has low precedence, the program is equivalent to:
(someValue ? ++x, ++y : --x), --y
Because conditional operator guaranteed the order of evaluate, the (++x, ++y) or –x will not be evaluated until someValue is evaluated. So now we have two situations:

  1. if someValue is true, then ++x , then ++y, then finally –y, because comma op need evaluate left operand first.
  2. if someValue is false, then –x, then finally –y.

The condition switches between (++x,++y) and (–x); no matter which one is going to be evaluated, –y will be evaluated at the end(guaranteed by the right-most comma operator).

ex.4.34
Exercise 4.34: Given the variable definitions in this section, explain whatconversions take place in the following expressions:

(a) if (fval) // float to bool, then bool to int
(b) dval = fval + ival; //int to float, then float to double
(c) dval + ival * cval; // char to int, then int to double

ex.4.35

Exercise 4.35: Given the following definitions,

char cval; int ival; unsigned int ui;
float fval; double dval;
identify the implicit type conversions, if any, taking place:
(a) cval = 'a' + 3; //char to int, then int to char(result truncated)
(b) fval = ui - ival * 1.0; //
(c) dval = ui * fval; //my PC: float 32, int 16. float is big enough to represent all unsigned int, then: unsigned int to float, then float to double
(d) cval = ival + fval + dval; //int to float, float to double, double to char(result truncated).

ex.4.36
Exercise 4.36: Assuming i is an int and d is a double write the expression i *= d so that it does integral, rather than floating-point, multiplication.

i *= static_cast<int> d;

ex.4.37
Exercise 4.37: Rewrite each of the following old-style casts to use a named cast:

int i; double d; const string *ps; char *pc; void *pv;
(a) pv = (void*) ps; 
pv = const_cast<void*> ps;
(b) i = int(*pc);
i = static_cast<int> *pc;
(c) pv = &d;
pv = static_cast<void> &d;
(d) pc = (char*) pv;
pc = reinterpret_cast<char> pv;

ex.4.38
Exercise 4.38: Explain the following expression:

double slope = static_cast<double>(j/i);
convert result of j / i to double type, then assgins to variable slope.