• Tidak ada hasil yang ditemukan

Shift Operators (aka IO Operators) Are Left Associative

Dalam dokumen Book C++ Primer Fifth Edition (Halaman 184-188)

Although many programmers never use the bitwise operators directly, most pro- grammers do use overloaded versions of these operators for IO. An overloaded operator has the same precedence and associativity as the built-in version of that operator. Therefore, programmers need to understand the precedence and associa- tivity of the shift operators even if they never use them with their built-in meaning.

Because the shift operators are left associative, the expression

ptg11539634

156 Expressions

cout << "hi" << " there" << endl;

executes as

( (cout << "hi") << " there" ) << endl;

In this statement, the operand"hi"is grouped with the first<<symbol. Its result is grouped with the second, and then that result is grouped with the third.

The shift operators have midlevel precedence: lower than the arithmetic oper- ators but higher than the relational, assignment, and conditional operators. These relative precedence levels mean we usually have to use parentheses to force the correct grouping of operators with lower precedence.

cout << 42 + 10; // ok:+has higher precedence, so the sum is printed cout << (10 < 42); // ok: parentheses force intended grouping; prints1 cout << 10 < 42; // error: attempt to comparecoutto42!

The lastcoutis interpreted as (cout << 10) < 42;

which says to “write10ontocoutand then compare the result of that operation (i.e.,cout) to42.”

E

X E R C I S E S

S

E C T I O N

4.8

Exercise 4.25: What is the value of~’q’ << 6on a machine with 32-bitints and 8 bit chars, that uses Latin-1 character set in which’q’has the bit pattern01110001?

Exercise 4.26: In our grading example in this section, what would happen if we used unsigned intas the type forquiz1?

Exercise 4.27: What is the result of each of these expressions?

unsigned long ul1 = 3, ul2 = 7;

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

4.9 The sizeof Operator

Thesizeofoperator returns the size, in bytes, of an expression or a type name.

The operator is right associative. The result ofsizeofis a constant expression (§ 2.4.4, p. 65) of typesize_t (§ 3.5.2, p. 116). The operator takes one of two forms:

sizeof (type) sizeof expr

In the second form,sizeofreturns the size of the type returned by the given ex- pression. Thesizeofoperator is unusual in that it does not evaluate its operand:

ptg11539634

Section 4.10 Comma Operator 157

Sales_data data, *p;

sizeof(Sales_data); // size required to hold an object of typeSales_data sizeof data; // size ofdata’s type, i.e.,sizeof(Sales_data)

sizeof p; // size of a pointer

sizeof *p; // size of the type to whichppoints, i.e.,sizeof(Sales_data) sizeof data.revenue; // size of the type ofSales_data’srevenuemember sizeof Sales_data::revenue; // alternative way to get the size ofrevenue The most interesting of these examples issizeof *p. First, becausesizeofis right associative and has the same precedence as*, this expression groups right to left. That is, it is equivalent to sizeof (*p). Second, becausesizeofdoes not evaluate its operand, it doesn’t matter thatpis an invalid (i.e., uninitialized) pointer (§ 2.3.2, p. 52). Dereferencing an invalid pointer as the operand tosizeof is safe because the pointer is not actually used. sizeofdoesn’t need dereference the pointer to know what type it will return.

Under the new standard, we can use the scope operator to ask for the size of a member of a class type. Ordinarily we can only access the members of a class through an object of that type. We don’t need to supply an object, becausesizeof does not need to fetch the member to know its size.

The result of applyingsizeofdepends in part on the type involved:

sizeof charor an expression of typecharis guaranteed to be 1.

sizeofa reference type returns the size of an object of the referenced type.

sizeofa pointer returns the size needed hold a pointer.

sizeofa dereferenced pointer returns the size of an object of the type to which the pointer points; the pointer need not be valid.

sizeofan array is the size of the entire array. It is equivalent to taking the sizeofthe element type times the number of elements in the array. Note thatsizeofdoes not convert the array to a pointer.

sizeofastringor avectorreturns only the size of the fixed part of these types; it does not return the size used by the object’s elements.

Becausesizeofreturns the size of the entire array, we can determine the num- ber of elements in an array by dividing the array size by the element size:

// sizeof(ia)/sizeof(*ia)returns the number of elements inia constexpr size_t sz = sizeof(ia)/sizeof(*ia);

int arr2[sz]; // oksizeofreturns a constant expression § 2.4.4 (p. 65)

Becausesizeofreturns a constant expression, we can use the result of asizeof expression to specify the dimension of an array.

4.10 Comma Operator

Thecomma operatortakes two operands, which it evaluates from left to right. Like the logicalANDand logicalORand the conditional operator, the comma operator guarantees the order in which its operands are evaluated.

ptg11539634

158 Expressions

E

X E R C I S E S

S

E C T I O N

4.9

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

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;

cout << sizeof(p)/sizeof(*p) << endl;

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

(a) sizeof x + y (b) sizeof p->mem[i]

(c) sizeof a < b (d) sizeof f()

The left-hand expression is evaluated and its result is discarded. The result of a comma expression is the value of its right-hand expression. The result is an lvalue if the right-hand operand is an lvalue.

One common use for the comma operator is in aforloop:

vector<int>::size_type cnt = ivec.size();

// assign values fromsize. . .1to the elements inivec for(vector<int>::size_type ix = 0;

ix != ivec.size(); ++ix, --cnt) ivec[ix] = cnt;

This loop incrementsixand decrementscntin the expression in theforheader.

Bothixandcntare changed on each trip through the loop. As long as the test of ixsucceeds, we reset the current element to the current value ofcnt.

E

X E R C I S E S

S

E C T I O N

4.10

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.

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) { /* . . . */ }

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

someValue ? ++x, ++y : --x, --y

ptg11539634

Section 4.11 Type Conversions 159

4.11 Type Conversions

In C++ some types are related to each other. When two types are related, we can use an object or value of one type where an operand of the related type is expected.

Two types are related if there is aconversionbetween them.

As an example, consider the following expression, which initializesivalto 6:

int ival = 3.541 + 3; // the compiler might warn about loss of precision The operands of the addition are values of two different types: 3.541has type double, and3 is anint. Rather than attempt to add values of the two differ- ent types, C++ defines a set of conversions to transform the operands to a com- mon type. These conversions are carried out automatically without programmer intervention—and sometimes without programmer knowledge. For that reason, they are referred to asimplicit conversions.

The implicit conversions among the arithmetic types are defined to preserve precision, if possible. Most often, if an expression has both integral and floating- point operands, the integer is converted to floating-point. In this case,3is con- verted todouble, floating-point addition is done, and the result is adouble.

The initialization happens next. In an initialization, the type of the object we are initializing dominates. The initializer is converted to the object’s type. In this case, thedoubleresult of the addition is converted tointand used to initialize ival. Converting adoubleto aninttruncates thedouble’s value, discarding the decimal portion. In this expression, the value6is assigned toival.

Dalam dokumen Book C++ Primer Fifth Edition (Halaman 184-188)