第4章 表达式
4.1 基础
左值和右值
当一个对象被用作左值的时候,使用的是对象的内存空间。当一个对象被用作右值的使用,使用的是对象的内存数值。
左值的同时,也可以当做右值,从而使用其值。但是右值,不一定能作为左值。
warming
赋值运算需要一个左值作为左侧运算对象,同时,也将返回一个左值。
decltype作用于左值,将返回一个引用类型,比如*p,返回的是左值,decltype(*p)得到是int&类型。decltype作用于右值,将返回一个指针类型,比如&p,返回的是右值,decltype(&p)得到是int**类型。
4.2 算术运算符(+ - * / %)
算术运算符按照从左向右的顺序进行组合,是左结合律。
算术运算符的求职结果是右值。
算术运算中,非数值需要转换成数值才能参与运算,例如bool值将分别转换为0、1.
对于%运算符,作用对象应为整数,并满足m=(m/n)*n+m%n。可知,m%n符号与m相同。
4.3 逻辑和关系运算符(>=<、==、!=、!、&&、||)
逻辑和关系运算符返回的类型都是布尔类型,运算对象和求值结果都是右值。
4.4 赋值运算符(=)
赋值运算符的左侧对象必须是一个可修改的左值。
赋值运算符返回的是他的左侧运算对象,并且是一个左值。
赋值运算符满足右结合律。
赋值运算符运算结果的用处:
int i; while( (i=get_value()) != 42) do_something();
4.5 递增递减(++、--)
前置,将运算对象递增/减,并返回运算之后的对象,作为左值。后置,将运算对象递增/减,并返回运算之前对象的副本,作为右值。
因此,如非必要,避免使用后置对象,可以减少产生副本的开销。
后置的用法:
auto pbeg = v.begin(); while(pbeg != v.end() ) //++优先级高,此处将当前值输出,并将迭代器提升一个位置 cout<< *pbeg++<<endl;
4.6 成员访问运算符(->、.)
ptr->men等价于(*ptr).mem
4.7 条件运算符(cond ? expr1 : expr2)
条件运算符使用右结合律,是从右向左组合的。右边的通常是左边的分支。
4.8 位运算
~ 按位求反
<<、>> 移位运算(有时事IO运算)
&, | 按位与、或
^ 按位异或
移位运算满足左结合律。优先级比关系运算、赋值运算、条件运算高,比算术运算低。
4.9 sizeof运算符
返回表达式或者类型名字占用的字节数。满足右结合律,所得为size_t类型的常量。有两种形式:
sizeof(type)
sizeof expr
sizeof并不直接计算表达式的值。
sizeof *p满足右结合律,sizeof和*的优先级相同,因此等价于sizeof(*p)。由于不直接计算表达式的值,所以即使p是一个空指针,仍然可以求值。
warming
sizeof对char表达式运算结果为1。
sizeof对数组运算结果为整个数组占用的字节数。
sizeof对string和vector只会返回类型固定部分的大小,不返回其所有元素占用空间的大小
4.10 逗号运算符(,)
从左向右求值,返回右侧的值。
4.11 类型转换
隐式转换
比int类型小的整型,提升为较大的整型。
条件中,非布尔值转换成布尔值。初始化中,初始值转换为变量的类型;赋值中,右侧类型转换成左侧类型。
算术运算、关系运算中,转换成同一种类型进行运算。字节相同,带符号的转换成无符号的。字节不同,转换成字节多的类型。
函数调用时。
数组转换成指针。
回头补充
编译器每次只会执行一种类类型的转换,但是内置类型的转换和类类型的转换可以一起使用。
显示转换
强制类型转换的形式:cast-name<type>(expression)
补充:const int *const p = &value,其中第二个const是顶层,限制p的内存不能更改;第一个const是底层,限制指向的内存不能改。
static_cast:只要不包含底层const,都可以使用。
const_cast:只能改变底层const性质,不能改变类型,也就是可以将常量转换为变量。
reinterpret_cast:提供对内存位模式的重新解释,例如将指向int的指针(int *ip),转换成指向char的指针(reinterpret_cast<char*>(ip) )。
int x(0x00484848); //十六进制48,表示H int *p=&x; char *c=reinterpret_cast<char*>(p); cout<< c <<endl; //输出为:HHH,依赖于机器,此处int的高位是内存中较高的地址。
dynamic_cast:运行时类型识别。
旧式强制类型转换
type(expr)
(type)expr