!!!Chapter 5 Expression

Operator precedence: http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B

5.1 Arithmetic Operators

An expression is composed of one or more operands that are combined by  operators.
The simplest form of an expression consists of a single literal constant or variable. 
Every expression yields a result. The result of an expression is an rvalue, we can read the result but cannot assign to it.
There are both unary operators and binary operators: unary operator act on one operand; binary operator act on two operands. 
Arithmetic operators
+, -Unary plus/minus+/- expr
*, /, %multi, division, remainderexpr * expr
+, -addition, subtractionexpr + expr
PS: listed by their precedence
unary minus will negates its operand, unary plus will return the value itself:
int i = 1024;
int k = -i;     //negates the value of its operand
Division between integers results in an integer. If the quotient contains a fractional part, it is truncated:
int ival1 = 21/6;    //quotient is truncated, result is 3
int ival2 = 21/7;    //no remainder, result is 3
% operator can only be applied to operands of integral types: bool, char, short, int, long, and associated unsigned type:
int ival = 42;
double ival2 = 3.14;
ival % 12;         //valid
ival2 % 12;        //invalid
How to decide sign when use division and modulus:
1. If both operands are positive, the result is positive (or zero)
2. If both operands are negative, the result of division is positive (or zero) and the result of modulus is negative (or zero).
3. If one is negative and the other is positive, the result is machine-dependent
-21%-8;      //result is -5
21%-5;       //machine-dependent: result is 1 or -4
-21/-8;      //result is 2
21/-5;       //machine-dependent: result is -4 or -5

5.2 Relational and Logical Operators

Relational and Logical Operators
!logical NOT!expr
<, <=, >, >=compareexpr < expr
==, !=equality, inequalityexpr == expr
&&logical ANDexpr && expr
||logical ORexpr || expr
1. listed by their precedence
2. These operators take operands of arithmetic or pointer type and return bool.
For logical AND and OR operators, we use " short-circuit evaluation": expr2 is evaluated if and only if expr1 does not by itself determine the result. A valuable use of the logical AND operator is to have expr1 evaluate to false in the presence of a boundary condition that would make the evaluation of expr2 dangerous:
string s("Example how to use And");
string::iterator it =s.begin();
while (it != s.end() && !isspace(*it)){   //first ensure it is inside the boundary
    *it = toupper(*it);
    ++it;
}
The relational operators do not chain together:
if (i < j < k);          //wrong, will return true when k > 0 or 1
if (i < j && j < k);     //ok
The bool value false converts to zero, and true converts to one, so we should not use true or false to determine the condition:
if(val == true);         //wrong, this equals to if (val == 1)
if (val);                //ok

5.3 The Bitwise Operators

Bitwise Operators
~bitwise NOT~expr
<<, >>left/right shiftexpr1 << expr2
&bitwise ANDexpr1 & expr2
^bitwise XORexpr1 ^ expr2
|bitwise ORexpr1 | expr2
1. listed by their precedence
2. bitwise operators take operands of integral type
3. the sign bit is handled machine-dependent, so we'd better use unsigned type when using integral value with the bitwise operators
NOT operator (~) is similar in behavior to the bitset flip operation:
unsigned char bits = 0227;     //10010111
bits = ~ bits;                 //01101000
Shift operator(<<, >>) will discard the bits that are shifted off the end. (The right-hand operand must not be negative and must be a value that is strictly less than the number of bits in the left-hand operand)
unsigned char bits = 0233;     //10011011
bits <<1;                      //00110110    the left most bit is discarded
And operator(&) will return 1 when both operands are 1; or 0.
XOR operator(^) will return 1 if either but not both operands contain 1 ; or 0.
OR operator(|) will return 1 if either or both operands are 1; or 0.
Bitset library are more direct easier than manipulate integral with bitwise operation.

Shift Operators has midlevel precedence:lower than arithmetic operators, but higher than relational, assignment, or conditional operators:
cout << 42 + 10;       //ok, + has higher precedence
cout << (10 < 42);     //ok, parentheses force intended grouping, result is 1
cout << 10 < 42;       //error, attempt to compare cout to 42 

5.4 Assignment Operators

The result of an assignment is the left-hand operand; the type of the result is the type of the left-hand operand. If the types of the left and right operands differ, we may convert the right side value;
int ival;
ival = 0;        //result: type int value 0
ival = 3.14;   //result: type int value 3
Assignment is right associative. If the types of objects in a multiple assignment are either the same or can be converted to one another, then we can perform multiple assignment in a single expression:
int ival; int *pval;
ival = pval = 0;       //error, cannot assign int with pointer
string s1, s2;
s1 = s2 = "ok";        //ok, use s2 to assign s1
Assignment has low precedence:
while ((i = get_value()) != 42);   //ok, compare i with 42
while (i = get_value() != 42);     //error, assign the test result to i
The general syntactic form of a compound operator is:
a op= b;   //equals to a = a op b
Possible compound assignment operators
arithmetic operators+=, -=, *=, /=, %=
bitwise operators<<=, >>=, &=, ^=, |=

5.5 Increment and Decrement Operators

There are two forms of increment/decrement operators: prefix and postfix. prefix will increase/decrease the operand and yield the changed value as its result; postfix will increase/decrease the operand but yield a copy of the original unchanged value as its result:
int i = 0, j;
j = ++i;        //j=1, i=1
j = i++;        //j=1, i=2
We prefer to use prefix!
We can combine dereference and increment in a single expression:
vector<int>::iterator iter = ivec.begin();
//print 10 9 ...
while (iter != ivec.end())
cout << *iter++ << endl;  //the precedence of postfix is higher than dereference

5.6 The Arrow Operator

the arrow operator(->) provides a synonym for expressions involving the dot and dereference operators:
(*p).foo;      //dereference p to get an object and fetch its member named foo
p -> foo;      //equivalent way to fetch the foo from the object to which p points
The arrow is always used when we need to use pointer and class objects:
item1.same_isbn (item2);    //item1 is a object of Sales_item class, same_isbn is a member of this class
Sales_item *p = &item1;
(*p).same_isbn (item2);    //correct, same as line 1
*p.same_isbn (item2);      //error, dot has higher precedence than dereference
p -> same_isbn (item2);    //correct, same as line 1 

5.7 The conditional Operator

The conditional operator is the only ternary operator in C++. The syntactic is:
cond ? expr1 : expr2;   //evaluate expr1 when cond is true, or vice verse
Only one of the expression is evaluated!
The conditional Operator has low precedence, we need to use parenthesize:
cout << (i < j ? i : j);        //correct
cout << i < j ? i : j;          //wrong, compare cout to int
cout << (i < j) ? i : j;        //print 1 or 0
//The third expression equals to:
cout << (i < j);
cout ? i : j;

5.8 The sizeof Operator

The sizeof operator returns a value of type size_t that is the size , in bytes, of an object or type name.
sizeof takes one of the following forms:
sizeof (type name);        //eg. sizeof (Sales_item)
sizeof (expr);             
sizeof expr;               //eg. sizeof item1
The return value of sizeof depends in part on the type involved:
1. sizeof char or an expression of type char is guaranteed to be 1
2. sizeof a reference type returns the size of the memory necessary to contain an object of the referenced type
3. 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
4. sizeof an array is equivalent of taking the sizeof the element type times the number of elements in the array
//We can get the number of elements by dividing the sizeof the array by the sizeof the element
int sz = sizeof (ia)/sizeof (*ia);   //ia is an array 
The sizeof(int) meant to be the natural size for the machine to use, which means the width of the register. (for 32-bit machine, it should be 4 = 32/8)
And sizeof(ptr) (The size of a pointer) should be the size of a variable that can hold an address. (for 32-bit machine, the memory size is 4GB, and the pointer is 32 bit)

5.9 Comma Operator

A comma expression is a series of expressions separated by commas. The expressions are evaluated from left to right. The result of a comma expression is the value of the rightmost expression.
// comma operator is often used in for-loop
for (int i=0, j =0; i<50; ++i, ++j)

5.10 Evaluating Compound Expressions

An expression with two or more operators is a compound expression.
Operator Precedence: P 170
wiki: http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B

5.10.3 Order of Evaluation

Only few operators specify the evaluation order: &&, ||, ?:
The order of operand evaluation matters if one subexpression changes the value of an operand used in another subexpression:
//The following expression is undefined
if (ia[index++] < ia[index]);
//Suppose index = 0, then
if (ia[0] < ia[0])    //rhs is evaluated first
if (ia[0] < ia[1])    //lhs is evaluated first
Here is one safe and machine-independent way to rewrite the above code:
if (ia[index] < ia [index + 1]){
//do something
}
++index;

5.11 The new and delete Expression

Initializing

new and delete can not only be used to dynamically allocate and free arrays (P134), but also to dynamically allocate and free single objects.
When we dynamically allocate an object, we specify a type but do not name the object; we use the pointer to access/manipulate that object:
int *pi = new int;  //pi points to dynamically allocated, unnamed, uninitialized int
We initialize dynamically allocated objects in the same way as we initialize variables (we must use direct initialize, cannot use copy initialize):
int i (1024);
int *pi = new int(1024);
string s(10, '9');
string *ps = new string(10, '9');
If we do not explicitly state an initializer, the class type object will be initialized using the default constructor; built-in type object will be uninitialized.
string *ps = new string;    //string is empty
int *pi = new int;          //pi points to uninitialized int
We can also value initialize a dynamically allocated object by following the type name by a pair of empty parenthesize:
1. For class type, value initialize is same as uninitialize. (Both will use default constructor)
2. For built-in type, there is difference:
int *pi = new int;         //*pi is uninitialized
int *pi = new int();       //*pi is initialized to 0
The () syntax must follow a type name not a variable to value initialization:
int x();    //declare a function named x with no arguments that returns an int
If memory is not enough, new expression will throw an exception named bad_alloc

Destroying

When the use of object is completed, we must explicitly return the object's memory to the free store(It's illegal to delete a pointer that addresses memory that was not allocated by new).
delete pi;
After deleting a pointer, the pointer becomes what is referred to as a dangling pointer. We should setting the pointer to 0 after the object it refers to has been deleted to make it clear that the pointer points to no object.

5.12 Type Conversions

Two types are related if there is a conversion between them
The built-in conversions among the arithmetic types are defined to preserve precision, if possible. So integral will be converted to floating-point value when both exist in one expression.
In the case of assignment, the type of the left-hand operand dominates, because it is not possible to change the type of the object on the left-hand side.
When convert double to an int, the decimal portion is discarded. (6.59 will become 6)

5.12.1 When Implicit Type Conversion Occur

Compiler will apply conversions for both built-in and class type objects as necessary:
1. In expression with operands of mixed types, the types are converted to a common type:
int ival;
double dval;
ival <= dval;        //ival is converted to double
2. An expression used as a condition is converted to bool:
int ival;
if (ival);          //ival converted to bool
while (cin);        //cin converted to bool
3. An expression used to initialize or assign to a variable is converted to the type of the variable:
int ival = 3.14;     //3.14 converted to int
int *ip;
ip = 0;              // the int 0 converted to a null pointer of type int*
4. Function call (chapter 7)
5. Pointer Conversions: when we use an array, the array is automatically converted to a pointer to the first element.
    The exception when array is not converted to a pointer: as the operand of the address-of(&)operator or of sizeof, or when using the array to initialize a reference to the array.
6. Arithmetic and bool conversion: when convert arithmetic to bool, non zero is true, zero is false. When convert bool to arithmetic value, true is one, false is zero.
7. Conversion to const: nonconst can be converted to const when we use a nonconst to initialize a reference to const object. We can also convert nonconst address to const pointer:
int i;
const int ci = 0;
const int &j = i;      //ok, convert non-const to reference to const int
const int *p = &ci;    //ok, convert address of ono-const to address of a const
8. Conversion defined by library types (convert cin into bool):
string s;
while (cin >> s);
When we have signed and unsigned value in one expression, the signed value will be converted to unsigned.

5.12.4 Explicit Conversions (unread)

An explicit conversion is spoken of as a cast and is supported by following set of named case operators: static_cast, dynamic_cast, const_cast, reinterpret_cast
The general form for the named cast notation is the following:
cast-name<type> (expression);

1.dynamic_cast: 18.2 (P 772)

2.const_cast: it will cast away the constness ofits expression:

我们可能调用了一个参数不是const的函数,而我们要传进去的实际参数确实是const的,但我们知道这个函数式不会对参数做修改的。于是我们需要使用const_cast去除const限定:

const char *pc_str;
char *pc = string_copy(const_cast<char*>(pc_str));   //function string_copy only take char* parameter
3. static_cast: static_castare useful when assigning a larger arithmetic type to a smaller type. Itinforms reader and compiler that we are aware of and are not concerned aboutthe potential loss of precision:
double d = 97.0;
//cast specified to indicate that the conversion is intended
char ch = static_cast<char>(d);    //if we did not use cast, we will get 

4.reinterpret_cast: reinterpret_cast generally performsa low-level reinterpretation of the bit pattern of its operands.
Avoid Cast

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值