笔记:
4.1 基础
对于那些没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。
算术运算符的运算对象和求值结果都是右值。
4.2 算术运算符
4.3 逻辑与关系运算符
4.4 赋值运算符
赋值运算符的优先级比关系运算符低,因此在条件语句中,赋值部分通常应该加上括号。
赋值运算符的左侧对象必须是左值,右侧运算对象可以是左值,也可以是右值。
4.5 递增和递减运算符
除非必须,否则尽量使用递增运算符和递减运算符的前置版本。
++i,将i的值加一之后返回i的值;i++,将i的值加一之后,返回i初始值的副本。
4.6 成员访问运算符
箭头运算符作用于一个指针类型的运算对象,结果是一个左值。
4.7 条件运算符
条件运算符的优先级非常低,但是比赋值运算符的优先级高。
4.8 位运算符
逻辑与&&,逻辑或||,逻辑非!;位与&,位或|,位非~。
移位运算符的优先级比算数运算符的优先级低,但比关系运算符、赋值运算符和条件运算符的优先级高。
4.9 sizeof运算符
返回一条表达式或者一个类型名字所占的字节数。
vector对象所占的默认字节为16,string对象所占的默认字节为28。在作者本人的机器中。
4.10 逗号运算符
4.11 类型转换
强制类型转换用static_cast,对于底层const用const_cast。
课后习题:
练习 4.1:表达式5+10*20/2 的求值结果是多少?
答:105。
练习 4.2:根据4.12 节中的表,在下述表达式的合理位置添加括号,使得添加括号后运算对象的组合顺序与添加括号前一致。
(a) *vec.begin()
(b) *vec.begin() + 1
//这里涉及到的运算符的优先级关系为首先是成员选择运算符.、函数调用运算符()
//其次是解引用运算符*,最后是加法运算符+。因此可以改成:
(a) * (vec.beging())
(b) (* (vec.begin())) + 1
练习 4.3:C++语言没有明确规定大多数二元运算符的求值顺序,给编译器优化留下了余地。这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,你认为这可以接受吗?请说出你的理由。
答:这是可以接受的。当C++中采⽤这种策略时,编译器可根据需要来即时抉择求值顺序,从⽽实现尽可能高的执行效率,并且程序员可以通过一些技巧来规避潜在执行缺陷。
练习 4.4:在下面的表达式中添加括号, 说明其求值的过程及最终结果。编写程序编译该(不加括号的)表达式并输出其结果验证之前的推断。
12 / 3 * 4 + 5 * 15 + 24 % 4 / 2
//添加括号说明运算顺序,执行结果为91
((12 / 3) * 4)) + (5 * 15) + ((24 % 4) / 2)
练习 4.5:写出下列表达式的求值结果。
(a) -30 * 3 + 21 / 5 //-86
(b) -30 + 3 * 21 / 5 //-18
(c) 30 / 3 * 21 % 5 //0
(d) -30 / 3 * 21 % 4 //-2
练习 4.6:写出一条表达式用于确定一个整数是奇数还是偶数。
num % 2 == 0
上述式子为真时,num是偶数;为假时,num是奇数。
练习4.7:溢出是何含义?写出三条将导致溢出的表达式。
答:当计算的结果超出类型所能表示的范围时,产生溢出。
int i = 2147483647 + 1 ;
int j = -100000 * 300000;
int k = 2015 * 2015 * 2015 * 2015;
练习 4.8:说明在逻辑与、逻辑或及相等性运算符中运算对象求值的顺序。
答:逻辑与、逻辑非运算符都是先计算左侧运算对象的值在计算右侧运算对象的值,并当且仅当左侧运算对象的值不能确定表达式结果时才计算右侧运算对象的值。逻辑与、逻辑或是C++中少数规定了求值顺序的运算符,除此之外,还有条件运算符?:,和逗号运算符,。对于相等性运算符,C++没有规定其求值顺序。
练习 4.9:解释在下面的if语句中条件部分的判断过程。
const char *cp = "Hello Word";
if (cp && *cp)
//首先检查cp,即指针的状态是否是有效的,其次在检查*cp,即cp指针指向的字符是否为空字符‘\0’。
//在本例中,cp是一个指向字符串的指针,是一个有效指针,所以为真;
//*cp为‘H’,也为真,所以整个表达式的结果为真。