一个表达式可由一个或多个运算对象(可能还是另一个表达式),运算符组成。返回一个结果,这个结果的类型由表达式所定义。 对于最简单的表达式可由字面值或者一个变量组成,同时返回值为这个变量或字面值,对于复杂的表达式,可由一元运算符,二元运算符等和几个具有表达式特征的运算对象组成,对于特殊的表达式,一个函数运算符即()加几个未知数量的运算对象组成。
表达式有两个作用:第一个就是定义了运算对象的做什么操作,以及执行顺序。第二个就是说明了是否对运算对象的环境发生改变。一个表达式如何求值取决有运算符的操作数,优先级,结合性,运算对象的求值顺序,以及运算对象之间的类型转换。一个表达式对运算对象的环境改变取决于运算符对运算对象的左右值和对结果的左右值是否有以求以及运算符的性质。所以,要了解一个表达式运算过程,就必须了解 运算符的操作数,优先级,结合性,运算对象的求值顺序,运算对象之间的类型转换,运算符对运算对象的左右值的要求。
运算符的优先级和结合性:
高优先级的运算符结合性比低优先级的运算符对运算对象的结合性更强,但是当两个运算符优先级相同时,则根据其结合性来分析。如果有括号,括号的优先级最大。
优先级决定了运算对象组合方式,结合性决定了运算对象的组合顺序。
Eg: cin>>a>>b
两个运算符都是>>所以优先级相同,但是>>的结合性是左结合,所以先算cin>>a 即将a先写入输入流中,然后返回对象cin,在运算 >>b将被写入输入流中在返回cin。
运算对象的求值顺序:
求值顺序规定了运算对象按照什么顺序求值,在大多数情况下,不能明确指定求值顺序。
参照博客
运算对象之间的类型转换:
一般在编译中存在隐式转化,但是如果想避开编译器的隐式转化,就要人为的介入,即执行显示转化。
一、隐式转化
1.在条件中,非布尔类型的转化成布尔类型
While(cin >> s)
IO库中istream类定义个从输入对象向布尔类型的转化机制。根据这一规则,cin自动的转化成布尔类型。所得到的布尔类型的值是什么取决于cin的状态,如果cin读入成功,则将转化成ture,如果遇到非法的字符或者文件结束符,将返回false。
2.初始化过程中,初始值转化成变量类型,在赋值语句中(包含复杂赋值语句),右侧运算对象转化成左侧运算对象类型。
Int i = 3;double d = 3,14;
I*=d;
将d转化成i的类型然后将d称道i里面去。
3.算术类型:
当两个运算对象类型不同时,运算对象总是转化成宽字符类型,当表达式既有浮点数又有整数时,将整数转化成浮点数类型。
(1).整数提升
总是负责将小整数类型提升到达整数类型。
Bool char unsigned char signed char short unsigned short signed short 等都能转化成int 如果放不下转化成 unsigned int。
对于 wchar_t char16_t char32_t 将转换成能放得下的较小的类型。
(2)无符号类型运算对象
<1>.首先惊醒类型提升,如果提升后类型匹配,则不再进行额外的转化,否则:
<2>.如果都为无符号,有符号类型,则将小类型运算对象的类型转化成大类型。
<3>.如果无符号类型不小于有符号类型,带符号类型抓化成无符号类型 注意类型范围
Eg: unsigned int i = 34, char j = -3;
Int a = i + j;
J 先提升到int类型,然后将int类型化化成unsigned int 由于是负数超出了unsigned int表示的范围,所以将对j按照unsigned 形式表示的书取余后的余数与i相加。
<4>.如果无符号类型小于有符号类型,则根据机器对类型表示的取值范围的限制。
如果在某机器中,无符号类型所有值都能在有符号中表示,则将无符号类型转化成那个有符号的类型,否则,将反之,对于一个无符号的所有值不能在有符号中表示,并且还将无符号类型转化成那个有符号的类型,则将发生未定义的行为。
4.数组转化成指针:
在大多数表达式中数组都能转化成指针,特别是在函数参数中时。但是在特殊场合下不会转化,比如:decltyppe参数 &运算 sizeof()以及 typid 和当一个引用赋值给数组时。
5.指针的转化:
常量整型值和nullptr能抓化成任意的指针类型。
指向任何的非常量的指针类型都能转化成void *类型
指向任意的类型指针都能转化成 const void *
再有继承关系的类型之间还有另一种指针的转化方式
6.转化成常量:
可将指向T指针类型或引用,分别转化成指向 const T 的指针或引用,反之不然。
7.类类型定义的转化:
二显示转化:
(1).命名类型的强制类型转化;
Const—name<type> (expression)
将表达式的值按照Const—name法则转化成type类型。
Const—name 有static_cast,dynamic_name const_cast reinterpret_cast
<1>Static_cast
任何明确的定义的类型转化,只要不包含底层const 都可以用 static_cast
在隐式转化中可以自动的将短类型转化成长类型,但是将大类型转化成小类型时将发生警报,但是用Static_cast将能消除警告。并且还能实现隐式转化的逆操作 比如将void *类型转化成其他类型。
<2>const_cast
const_cast只改变运算对象的底层const机制但是不能改变实际类型,可以通过与Static_cast复合使用来改变。
如果对象本身不是一个常量,改变后 进行写操作是合法的,如果本事是个常量,就不允许写操作了。
<3>reinterpret_cast
为运算对象的位模式提供较低层次上的重新解释
(2)旧式的类型转化
Type(expr)
(type) exper
由于旧式的强制类型转化可以替代命名的类型转化作用,但在表现形式不那么清晰了,如果追踪一个程序某个变量到底转化成什么类型很难发现,这也是 命名的类型转化好处,可读性高,减少不定向的类型转化。