What is the value returned by 5 + 10 * 20/2?
105
Using Table 4.12 (p. 166), parenthesize the following expressions to indicate the order in which the operands are grouped:
* vec.begin() //=> *(vec.begin()) * vec.begin() + 1 //=> (*(vec.begin())) + 1
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?
Yes, I think it necessary to hold the trade-off. Because the speed is always the biggest advantage of C++. Sometimes, we need the compiler's features for efficient work. But if you are not a expert. I have to advice you do not touch the undefined behaviors.
For an instance, cout << i << ++i <<endl
should never appear in your code.
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 // parenthesize ((12 / 3) * 4) + (5 * 15) + ((24 % 4) / 2) // 16 + 75 + 0 = 91 // print: 91
Determine the result of the following expressions.
-30 * 3 + 21 / 5 // -90+4 = -86 -30 + 3 * 21 / 5 // -30+63/5 = -30+12 = -18 30 / 3 * 21 % 5 // 10*21%5 = 210%5 = 0 -30 / 3 * 21 % 4 // -10*21%4 = -210%4 = -2
Write an expression to determine whether an int value is even or odd.
i % 2 == 0 ? "even" : "odd"
What does overflow mean? Show three expressions that will overflow.
short svalue = 32767; ++svalue; // -32768 unsigned uivalue = 0; --uivalue; // 4294967295 unsigned short usvalue = 65535; ++usvalue; // 0
Explain when operands are evaluated in the logical
AND
, logicalOR
, and equality operators.
from the book:
The logical
AND
andOR
operators always evaluate their left operand before the right. Moreover, the right operand is evaluated if and only if the left operand does not determine the result. This strategy is known as short-circuit evaluation.
- logical
AND
: the second operand is evaluated if and only if the left side istrue
. - logical
OR
: the second operand is evaluated if and only if the left side isfalse
- equality operators :
true
only if both operands have the same value, otherwise, it returnsfalse
.
Explain the behavior of the condition in the following
if
:const char *cp = "Hello World"; if (cp && *cp)
cp is a pointer to const char *
, and it's not a nullptr
. true.
*cp
is a const char
: 'H'
, and it is explicit a nonzero value. true.
true && true => true.
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)
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
Assuming
i
,j
, andk
are all ints, explain whati != j < k
means.
According to Operator precedence, i != j < k
is same as i != (j < k)
.
The condition group j
and k
to the <
operator. The bool
result of that expression is the right hand operand of the !=
operator. That is i
(int) is compared to the true/false
result of the first comparison! To accomplish the test we intended, we can rewrite the expression as follows:
i != j && j < k
reference:
It is usually a bad idea to use the boolean literals
true
andfalse
as operands in a comparison. These literals should be used only to compare to an object of typebool
.
What are the values of i and d after each assignment?
int i; double d; d = i = 3.5; // i = 3, d = 3.0 i = d = 3.5; // d = 3.5, i = 3
Explain what happens in each of the if tests:
if (42 = i) // compile error: expression is not assignable if (i = 42) // true.
The following assignment is illegal. Why? How would you correct it?
double dval; int ival; int *pi; dval = ival = pi = 0; // pi is a pointer to int. // can not assign to 'int' from type 'int *' // correct it: dval = ival = 0; pi = 0;
Although the following are legal, they probably do not behave as the programmer expects. Why? Rewrite the expressions as you think they should be.
if (p = getPtr() != 0) if (i = 1024) // why? always true. use an assigment as a condition. // correct it if ((p = getPtr()) != 0) if (i == 1024)
Explain the difference between prefix and postfix increment.
The postfix operators increment(or decrement) the operand but yield a copy of the original, unchanged value as its result.
The prefix operators return the object itself as an lvalue.
The postfix operators return a copy of the object's original value as an rvalue.
What would happen if the while loop on page 148 that prints the elements from a vector used the prefix increment operator?
It will print from the second element and dereference v.end()
at last.(It's undefined and very dangerous)
Given that
ptr
points to anint
, thatvec
is avector<int>
, and thatival
is anint
, explain the behavior of each of these expressions. Which, if any, are likely to be incorrect? Why? How might each be corrected?ptr != 0 && *ptr++ // check ptr is not a nullptr. and check the pointer value. ival++ && ival // check ival and ival+1 whether equal zero. vec[ival++] <= vec[ival] // incorrect. It is an **UB** // correct: vec[ival] <= vec[ival+1]
Every value computation and side effect of the first (left) argument of the built-in logical AND operator && and the built-in logical OR operator || is sequenced before every value computation and side effect of the second (right) argument. see order of evaluation.
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.*iter++; // return *iter, then ++iter. (*iter)++; // illegal, *iter is a string, cannot increment value. *iter.empty() // illegal, iter should use '->' to indicate whether empty. iter->empty(); // indicate the iter' value whether empty. ++*iter; // illegal, string have not increment. iter++->empty(); // return iter->empty(), then ++iter.
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" ;
Operator Precedence: ?:
< +
Fix it:
string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ;
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.
if the operator were left associative.
finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";
would same as :
finalgrade = ((grade > 90) ? "high pass" : (grade < 60)) ? "fail" : "pass";
if grade > 90
, first conditional operator's result is high pass
. so the finalgrade is always fail. It's contradictory obviously.
What is the value of
~'q' << 6
on a machine with 32-bitint
s and 8 bitchar
s, that uses Latin-1 character set in which 'q
' has the bit pattern01110001
?
We see the value of ~ 'q'
is negative. Left-shifting a negative value has undefined behaviour.
check https://stackoverflow.com/questions/19585507/c-not-bitwise-operator-binary-char-conversion
In our grading example in this section, what would happen if we used unsigned int as the type for quiz1?
There are 30 students in the example of this section. unsigned long
have at least 32 bits on any machine. Thus, it is ensured that the bits start out with well-define values. The standard define that unsigned int
is guaranteed to hold 16 bits. If unsigned int
adopted, the result is undefined.
What is the result of each of these expressions?
unsigned long ul1 = 3, ul2 = 7; ul1 & ul2 // == 3 ul1 | ul2 // == 7 ul1 && ul2 // == true ul1 || ul2 // == ture
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; cout << sizeof(p)/sizeof(*p) << endl;
The first result is 10. It returns the number of elements in x. But the second result depends on your machine. It would be 2 on the 64-bit machine and 1 on the 32-bit machine. Because of the size of pointer is different on various machines.
reference: Why the size of a pointer is 4bytes in C++
Using Table 4.12 (p. 166), parenthesize the following expressions to match the default evaluation:
sizeof x + y // (sizeof x)+y . "sizeof" has higher precedence than binary`\+`. sizeof p->mem[i] // sizeof(p->mem[i]) sizeof a < b // sizeof(a) < b sizeof f() // if`f()`returns`void`, this statement is undefined, otherwise it returns the size of return type.
reference: sizeof operator
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 use prefix and not postfix, just because of the Advice: Use Postfix Operators only When Necessary
on 4.5. Increment and Decrement Operators
.
Advice: Use Postfix Operators only When Necessary
Readers from a C background might be surprised that we use the prefix increment in the programs we've written. The reason is simple: The prefix version avoids unnecessary work. It increments the value and returns the incremented version.The postfix operator must store the original value so that it can return the unincremented value as its result. If we don’t need the unincremented value, there’s no need for the extra work done by the postfix operator.
For ints and pointers, the compiler can optimize away this extra work. For more complicated iterator types, this extra work potentially might be more costly. By habitually using the prefix versions, we do not have to worry about whether the performance difference matters. Moreover—and perhaps more importantly—we can express the intent of our programs more directly.
So, it's just a good habits. And there are no changes if we have to be made to use the postfix versions. Rewrite:
for(vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt--)
ivec[ix] = cnt;
This is not an appropriate example to discuss the difference of prefix and postfix. Look at the section Built-in comma operator
on this page.
reference: Usage of the Built-in Comma Operator
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) { /* ... */ }
ptr
and ix
have the same function. The former use a pointer, and the latter use the index of array. we use the loop to through the array.(just choose one from ptr
and ix
)
Using Table 4.12 (p. 166) explain what the following expression does:
someValue ? ++x, ++y : --x, --y
Because of the most lowest precedence of the comma operator, the expression is same as:
(someValue ? ++x, ++y : --x), --y
If someValue is true, then ++x
, and the result is y
, if someValue is false, then --x
, and the result is --y
. so it is also same as:
someValue ? (++x, y) : (--x, --y);
Even though the result has nothing to do with x
, the evaluation of someValue
does effect the operation on x
.
Given the variable definitions in this section, explain what conversions take place in the following expressions:
(a) if (fval) (b) dval = fval + ival; (c) dval + ival * cval;
Remember that you may need to consider the associativity of the operators.
if (fval) // fval converted to bool
dval = fval + ival; // ival converted to fval, then the result of fval add ival converted to double.
dval + ival * cval; // cval converted to int, then that int and ival converted to double.
Given the following definitions,
char cval; int ival; unsigned int ui; float fval; double dval;identify the implicit type conversions, if any, taking place:
cval = 'a' + 3; // 'a' promoted to int, then the result of ('a' + 3)(int) converted to char. fval = ui - ival * 1.0; // ival converted to double , ui also converted to double. then that double converted(by truncation) to float. dval = ui * fval; // ui promoted to float. then that float converted to double. cval = ival + fval + dval; // ival converted to float, then that float and fval converted to double. At last, that double converted to char(by truncation).
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);
Rewrite each of the following old-style casts to use a named cast:
int i; double d; const string *ps; char *pc; void *pv; pv = (void*)ps; // pv = const_cast<string*>(ps); or pv = static_cast<void*>(const_cast<string*>(ps)); i = int(*pc); // i = static_cast<int>(*pc); pv = &d; // pv = static_cast<void*>(&d); pc = (char*)pv; // pc = reinterpret_cast<char*>(pv);
Explain the following expression:
double slope = static_cast<double>(j/i);
j/i
is an int(by truncation), then converted to double and assigned to slope.