Chapter 5:Expressions

Section 5.1 ArithmeticOperators

  • For both division (/) and modulus (%), when both operands are postive or negative, the result is positive (or zero), the modulus of negatives is negative (or zero). If only one operand is negative, then the result of modulus is machine-dependent for both operators.

Exercise 5.1: Parenthesize the following expressions to indicate how it is evaluated. Test your answer by compiling the expression and printing its result.

12 / 3 * 4 + 5 *15 +24 % 4 / 2

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

Exercise 5.2: Determine the result of the following epressions and indicate which results, if any, are machine-dependent.

(a) -30 * 3 + 21 / 5

(b) -30 + 3 * 21 / 5

(c) 30 / 3 * 21 % 5

(d) -30 / 3 * 21 % 4

(a) Result is 86.
(b) Result is -18.
(c) Result is 0.
(d) Machine-dependent: Result is -2 or 2.

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

#include <iostream>

using std::cout;
using std::cin;

int main() {
    cout << "Enter a int value to check if it is even or odd.\n"; 
    int ival;
    cin >> ival;

    if (ival % 2)
      cout << ival << " is odd." << std::endl;
    else
      cout << ival << " is even." << std::endl;    

    return 0; 
}

Exercise 5.4:Define the term overflow. Show three expressions that will overflow.

// overflow is to exceed the range of the type can hold.
#include <iostream>

int main() {
    short sval = 32768;
    int ival = 214733647*20;
    unsigned uival = 0 - 1;    
    std::cout << sval << std::endl
          << ival << std::endl
          << uival << std::endl;      
    std::cin.get();

    return 0; 
}

Section 5.2 Relational and Logical Operators

Logical AND and OR Operators

  • The logical AND and OR operations always evaluate their left operand before the right. The right operand is evaluated only if the left operand does not determine the result.This

    evaluated only if the left operand does not determine the result. This evaluation strategy is often referenced the expression to as "short-circuit evaluation.


The Relational OPerators Do Not Chain Together

  • The relational operators (<, <=, >, >=) are left associative.
  • Because the relational operators return bool results so we shouldn't chain them together when we need to compare the relations of three values.

Equality Tests and the bool literals

  • It is almost never right to write an equality test that tests against the bool literal true, because bool converts to one.

EXERCISE SECTION 5.2

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

1 From the left to right.
2 In logcial AND, expr2 will be evaulated if expr1 is true.
3 In logcial OR, expr2 will be evaulated if expr1 is false.
4 In equality operator, all will be evaulated.

Exercise 5.6: Explain the behaviour of the following while condition:

char *cp = "Hello, World";

while (cp && *cp)

1 The while condition will be executed.

Exercise 5.7: 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.

1 int ival;
2 cin >> ival;
3 while (ival != 42)
4     cin >> ival;

Exercise 5.8: 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.

1 if (a > b && b > c && c > d)
2     return true;
3 else
4     return false;

Section 5.3 The Bitwise Operators

  • Because there are no guarantees for how the sign bit is handled, we strongly recommend usingan unsigned type when using an integral value with the bitwise operators
  • The shift operators will discard the bits that are shifted off the end.

  • If the operand is signed, it can either insert copies of the sign bit or insert 0-valued bits, which one it uses it implematation defined.

5.3.1 Using bitset Objects or Integral Values

  • In general, the library bitset operations are more direct, easier to read, easier to write, and more likely to be used correctly. Moreover, the size of a bitset is not limited by the number of bits in an unsigned. Ordanarily bitset should be used in preference to lower-level direct bit manipulation of integral values.

Exercise 5.9: Assume the following two definitions:

unsigned long ul1 = 3, ul2 = 7;

What is the result of each of the following expressions?

(a) ul1 & ul2 (c) ul1 | ul2

(b) ul1 && ul2 (d) ul1 || ul2

(A) 3
(B) 7
(C) 1
(D) 1



5.3.2 Using the Shift Operators for IO

The IO Operators Are Left Associative

Section 5.4 Assignment Operators

  • The left-hand operand of an assignment must be a nonconst lvalue.

5.4.1 Assginment is Right Associative

5.42 Assignment Has Low Precedence

  • The additional parentheses around the assignment are necessary because assignment has lower precedence than inequality.

Beware of Confusing Equality and Assignment Operators

Exercise 5.11: What are the values of i and d after the each assignment:

int i; double d;

d = i = 3.5;

i = d = 3.5;

i= 3, d =3.0
d = 3.5, i = 3

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

if (42 = i) // . . .

if (i = 42) // . . .

error: literals are rvalue.
ok: assign i to 42, always legal.

5.4.2Compound Assignment Operators

  • When we write "a op= b;" the left hand operand is evaluated only once, if we write "a = a op b;" that operand is evaluated twice: once as the right-hand operand and again as the left.

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

double dval; int ival; int *pi;

dval = ival = pi = 0;

Cannot assign a int pointer to a int.
dval = ival = 0;
pi = 0;

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

(a) if (ptr = retrieve_pointer() ! = 0)

(b) if (ival = 1024)

(c) ival += ival + 1;

(A) ptr only have the value 0 or 1.
     if ((ptr = retrieve_pointer()) ! = 0)
(B) The if statement will always be executed.
     if (ival == 1024)
(C) ival will be increased by itself plus one.
     ival += 1;

Section 5.5 Increment and Decrement Operators

  • The prefix version does less 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, so for more complex iterator types, this extra work potentially could be more costly.

Postfix Operators Return the Unicremented Value

Combining Deference and Increment in a Single Expression

  • The expression iter++ is usually very confusing to programmers new to both C++ and C, the precedence of postfix increment is higher than that of the dereference operator, so iter++ is equivalant to (iter++) and it is common among C++ programmers in iter++.

Exercise 5.15: Explain the difference between prefix and postfix increment.

Prefix version increments the value and returns the incremented version.
Postfix version must store the original value so that it can return the unincremented value as its result.

Exerise 5.16: Why do you think C++ wasn't named ++C

Because it is powered and give you a powerful one.

Exercise 5.17: What would happen if the while loop that prints the contents of a vector used the prefix increment operator.

It will output one value less.



Section 5.6 The Arrow Operator

Exercise 5.18: Write a program that defines a vector of pointers to strings. Read the vector, printing each string and its corresponding size.

#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{    
    vector<string*> svec(10);

    string str;
    while (cin >> str) {
    string *pstr = new string;
    *pstr = str;
    svec.push_back(pstr);
    }

    for (vector<string*>::iterator iter = svec.begin();
                   iter != svec.end(); ++iter)
    cout << **iter << " " << (*iter)->size() << endl;

    for (vector<string*>::iterator iter = svec.begin();
                   iter != svec.end(); ++iter)
    delete *iter;

    return 0;
}

Exercise 5.19: Assuming that iter is a vector::iterator, indicate which, if any, of the following expressions is legal. Explain the behavior of the legal expressions.

(a) iter++; (b) (iter)++;

(c) *iter.empty() (d) iter->empty();

(e) ++*iter; (f) iter++->empty();

(A) increments iter and then return the string.
(B) return the string and then increments iter.
(C) illegal, no member in std::vector<std::basic_string<char> >::iterator
(D) tell if the object is empty by return 0 or 1
(E) illegal, no ++ operator in string
(F) check if the object is empty and then increments iter.

Section 5.7 The Conditional Operator

  • The conditional operator is the only ternary operator in C++.

Avoid Deep Nesting of the Conditional Operator

Using a Conditional Operator in an Output Expression

Exercise 5.20: Write a program to prompt the user for a pair of numbers and report which is smaller.

#include <iostream>

using std::cin;
using std::cout;

int main()
{    
    int max, ival;

    cin >> max;
    while (cin >> ival)
    max = max > ival ? max : ival;

    cout << max;

    return 0;
}

Exercise 5.21: Write a program to process the elements of a vector. Replace each element with an odd value by twice that value.

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::vector;

int main()
{
    vector<int> ivec;
    int ival;
    while (cin >> ival)
    ivec.push_back(ival);

    for (vector<int>::iterator iter = ivec.begin();
                   iter != ivec.end(); ++iter)
    cout << ((*iter % 2 == 0) ? *iter : (*iter *= 2)) << endl;

    return 0;
}

Section 5.8 The sizeof Operator

  • sizeof char or an expression of type char is guaranteed to be 1.
  • sizeof a reference type returns the size of the memorynecessary to contain an object of the referenced type.

  • sizeof a pointer returns the size needed hold a pointer; to obtain the size of the object to which the pointer points, the pointer must be dereferenced

  • sizeof an array returns the size of the entire array, we can determine the number of elemnents by dividing the sizeof the array by the size of an element.

Exericise 5.22: Write a program to print the size of each of the built-in types.

#include <iostream>

using std::cout;
using std::endl;

int main()
{
    cout << sizeof (int) << endl
     << sizeof (double) << endl
     << sizeof (char) << endl
     << sizeof (bool) << endl;

    return 0;
}

Exercise 5.23: Predict the output of the following program 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 elements in array x.
Result of the size needed hold a int pointer divide by the size of a int.

Section 5.9 Comma Operator

  • The expression separated by commas are evaluated from left to right and result of a comma expression is the value of the rightmost expression.
  • One common use for the comma opearator is in a for loop.

Exercise 5.24: The program in this section is similar to the program on page 163 that added element to a vector. Both programs decremented a counter to generate the element values. In this program we used the prefix decrement and the earlier one used postfix. Explain why we used prefix in one and postfix in the other.

The previous program need to add 10 values as 10 to 1. So the cnt should first return the value then decremented.
This program only needs to increment and decrement the value of ix and cnt. The prefix does less work so not the postfix.

Section 5.10 Evaluating Compound Expression

  • Precedence specifies how the operands are grouped. It says nothing about the order in which the operands are evaluated. In most cases, operands may be evaluated in whatever order is convenient.

5.10.1 Precedence

  • The arithmetic operators are left associative.

Parentheses Override Precedence

  • Parenthesized expressions are evaluated by treating each parenthesized subexpression as a unit and otherwise applying the normal precedence rules.

5.10.2 Associativity

  • Associativity specifies how to group operators at the same precedence level.

Exercise 5.25: Using Table 5.4 (p.170), parenthesize the following expressions to indicate the order in which the operands are grouped:

(a) ! prt == ptr->next

(b) ch = buf[ bp++ ] != '\n'

(A) (! ptr) == (prt->next)
(B) ch = ((buf[ bp++ ]) != '\n')

Exercise 5.26:The expressions in the previous exercise evaluate in an order that is likely to be surprising. Parenthesize these expressions to evaluate in an order you imagine is intended.

(A) Same as the fact.
(B) ch = ((buf[ (bp++)]) != '\n')

Exercise 5.27: The following expression fails to compile due to operator precendence. Using Table 5.4 (p.170), explain why it fails. How would you fix in it?

string s = "word";

// add an 's' to the end, if the word doesn't already end in 's'

string pl = s + s[s.size() - 1] == 's' ? "" : "s";

It won't add an 's', whatever it is already end in 's'.
string pl = s + (s[s.size() - 1] == 's') ? "" : "s";

5.10.3 Order of Evaluation

  • The only operators that guarantee the order in which operands are the conditional (? :), comma operators, && and || operators. In all other cases, the order is unspecified.
  • The order of operand evaluation often, perhaps even usually, doesn't matter. It can matter greatly, though, if the operands refer to and change the same objects.

  • Do not use an increment or decrement operator on the same object in more than two subexpressions of the same expression.

Exercise 5.28: With the exception of the logical AND and OR, the order of evaluation of the binary operaters is left undefined to permit the compiler freedom to provide an optimal implementation. The trade-off is between an efficient implementation and a potential pitfall in the use of the language by the programmer. Do you consider that an acceptable trade-off? Why or why not?

Yes, programming is considered to be an interesting thing so it should be more flexible.

Exercise 5.29: Given that ptr points to aclss with an int menber named ival, vec is a vector holding ints, and that ival, jval, and kval are also ints, explains the behavior of each of these expressions. Which, if any, are likely to be incorrect? Why? How moght each be corrected?

(a) ptr->ival != 0 (b) ival != jval < kval

(c) ptr != 0 && *ptr++ (d) ival++ && ival

(e) vec[ival++] <= vec[ival]

(A) Return if ival in the class pointed by ptr equals to 0.
(B) Check ival connects with whether jval < kval, it perhaps is to check kval connects with whether ival != jval, (ival != jval) < kval
(C) Undefined, it perhaps return if ptr !=0 and *(ptr+1) is true,then increments ptr, ptr != 0 && *(ptr + 1); ++ptr;
(D) Undefined, it perhaps return if ival + 1 and ival is true, then increments ival, ival + 1 && ival; ++ival;
(E) Undefined, it perhaps return if vec[ival + 1] <= vec[ival], then invrements ival, vec[ival + 1] <= vec[ival]; ++ival;

Section 5.11 The new and delete Expressions

Initializing Dynamically Allocated Objects

Default Initialization of Dynamically Allocated Objects

  • When we do not explicitly state an initializer, then a dynamically allocated object is initialized in the same way as is a variable that is defined inside a function. If the object is of class type, it is initialized using the default constructor for the type; if it is of build-in type, it is unitialized.
  • It is (almost) always a good idea to initialize dynamically allocated objects.

  • The () syntax for value initialization must follow a type name, not a variable.

Memory Exhuastion

Destroying Dynamically Allocated Objects

  • It is illegal to apply delete to a pointer that addresses memory that was not allocated by new.

delete of a Zero-Valued Pointer

Resetting the Value of a Pointer after a delete

  • Setting the pointer to 0 after the object it refers to has been deleted makes it clear that the pointer points to no object.

Dynamic Allocation and Deallocation of const Objects

Deleting a const Object

Exercise 5.30: Which of the following, if any, are illegal or in error?

(a) vector svec(10); (f) delete svec;

(b) vector *pvec1 = new vector(10); (g) delete pvec1;

(c)vector **pvec2 = new vector[10]; (h) delete [] pvec2;

(d)vector *pv1 = &svec; (i) delete pv1;

(e)vector *pv2 = pvec1; (j) delete pv2;

(a) Legal.
(b) Legal.
(c) Illegal.
(d) legal.
(e) Legal.
(f) Illegal.
(g) Legal.
(h) Legal
(i) Legal.
(j) legal.

Section 5.12 Type Conversions

  • The built-in conversions among the arithmetic types are defined to preserve precision.

5.12.1 When Implicit Type Conversions Occur

  • In expressions with operands of mixed types, the types are converted to a common type.
  • An expression used as a condition is converted to bool.
  • An expression used to initialize or assign to a variable is converted to the type of the variable.

5.12.2 The Arithmetic Conversions

  • The simplest kinds of conversion are integral promotions.

Conversions between Signed and Unsigned Types

  • When an unsigned value is invovled in an expression, the conversion rules are defined to preserve the value of the operands.

Understanding the Arithmetic Conversions

  • In most of the following examples, either the operands are converted to the largest type invovled in the expression or, in the case of assignment expressions, the righthand operand is converted to the type of the left-hand operand:

bool flag; char cval;

short sval; unsigned short usval;

int ival; unsigned int uival;

long lval; unsigned long ulval;

float fval; double dval;

3.14159L + 'a'; // promote 'a' to int, then convert to long double

dval + ival; // ival converted to double

dval + fval; // fval converted to double

ival = dval; // dval converted (by truncation) to int

flag = dval; // if dval is 0, then flag is false, otehrwise true

cval + fval; // cval promoted to int, that int converted to float

sval + cval; // sval and cval promoted to int

cval + lval; // cval converted to long

ival + ulval; // ival converted to unsigned long

usval + ival; // promotion depends on size of unsigned short and int

uival + lval; // conversion depends on size of unsigned int and long

5.12.3 Other Implicit Conversions

Pointer Conversions

  • In most cases when we use an array, the array is automatically converted to a pointer to the first element.

Conversions to bool

  • Arithmetic and pointer values can be converted to bool. If the pointer or arithmetic value is zero, then the bool is false; any other value converts to true.
  • The null character has value zero and converts to false, all other char values convert to true.

Arithmetic Type and bool Conversions

  • When an arithmetic type is converted to bool, zero converted to bool, zero converts as false and any other value converts as true. When a bool is converted to an arithmetic type, true becomes one and false becomes zero.

Conversions and Enumeration Types

Conversions to const

  • A nonconst object can be converted to a const object, which happens when we use a nonconst object to initialize a reference to const object.

Conversions Defined by the Library Types

Exercise 5.31: Given the variable definitions on page 180, explain what conversions take place when evaluating the following expressions:

(a) if (fval)

(b) dval = fval + ival;

(c) dval + ival + cval;

Remember that you may need to consider associativity of the operators in order to determine the answer in the case of expressions involving more than one operator.

(a) fval is converted to bool.
(b) On the right. ival is converted to float and then the result is converted to double.
(c) ival is converted to double, and then cval is converted to double.

5.12.4 Explicit Conversions

  • An explicit conversion is spoken of as a cast and is supported by the following set of named cast operators: static_cast, dynamic_cast, const_cast, reinterpret_cast.
  • Although necessary at times, casts are inherently dangerous constructs.

5.12.5 When Casts Might Be Useful

  • One reason to perform an explicit cast is to override the usual standard conversions
  • Another reason for an explicit case is to select a specific conversion when more than one conversion is possible.

5.12.6 Named Casts

  • The general form for the named cast notation:

cast_name (expression);

dynamic_cast

  • A dynamic_cast supports the run-time identification of objects addressed either by a pointer or reference.

const_cast

  • A const_cast, as its name implies, cast away the constness of its expression.

static_cast

  • Any type conversion that the compiler performs implicitly can be explicitly requested by using a static_cast.
  • A static_cast is also usefull to perform a conversion that the compiler will not generate automatically.

reinterpret_cast

  • A reinterept_cast generally performs a low-level reinterpretation of the bit pattern of its operands.
  • A reinterept_cast is inherently machine-dependent. Safely using reinterpret_cast requires completely understanding the types involved as well as the details of how the compiler implements the cast.

5.12.7 Old-Style Casts

  • Although the old-style cast notation is supported by Standard C++, we recommend it can be used only when writing code to be compiled either under the C language or pre-standard C++.
  • The old-style cast notation takes one of the following two forms:

type (expr); // Function-style cast notation

(type) expr; // C-language-style cast notation

Exercise 5.32: Given the following set of definitions,

char cval; int ival; unsigned int ui;

float fval; double dval;

identifty the implicit type conversions, if any, taking place:

(a) cval = 'a' + 3; (b) fval = ui - ival * 1.0;

(c) dval = ui * fval; (d) cval = ival + fval + dval;

(a) 'a' is converted to int and then the result of 'a' + 3 is converted to char.
(b) ival is converted to double, next ui is converted to double and then the result of ui - ival * 1.0 is converted to float.
(c) ui is converted to float, then the result of ui * fval is converted to double.
(d) ival is converted to float, next ival + fval is converted to double, then the result of ival + fval + dval is converted to char.

Exercise 5.33: Given the following set of defintions,

int ival; double dval;

const string ps; char pc; void *pv;

rewrite each of the following using a named cast notation:

(a) pv = (void) ps; (b) ival = int(pc);

(c) pv = &dval; (d) pc = (char*) pv;

(a) pv = static_cast<void*> (const_cast<string*> (ps));
(b) ival = static_cast<int> (*pc);
(c) pv = static_cast<void*> (&dval);
(d) pc = static_cast<char*> (pv);

原文链接: https://www.cnblogs.com/alldots/archive/2012/04/29/2476233.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/48901

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 上午12:41
下一篇 2023年2月9日 上午12:42

相关推荐