摘要
本课结束本课程的第三单元,这一单元讲表达式及其运算符。本课重建一条概念链:从算术运算符到库函数,途经表达式树和每个节点的类型、条件及其代数、短路求值、副作用和求值顺序,以及位;把其中在任何语言中都成立的内容与 C++ 特有的内容区分开;汇总本单元的未定义行为,以及编译器能发现的、检测器能发现的和谁也发现不了的;在结业测评中重复入门测评的各项技能;最后给出六道综合整个单元的挑战题。
学习目标
- 根据表达式树和每个节点上的转换,预测本课程中任何表达式的值、类型和作用。
- 写出复合条件并对其取反,用短路求值保护一个否则就没有意义的运算。
- 识别没有意义的表达式(本单元的未定义行为目录),说出能发现每一种的工具,并把它们改写成没有未定义行为的形式。
- 针对给定的计算,在算术、位运算符和数学库之间作出选择,并说明理由。
- 在本单元所学内容中,把在任何语言中都成立的内容与 C++ 特有的内容区分开。
本单元的收获
本单元的目标是教人构造其值可以确切预测的表达式:哪个运算先做,结果是什么类型,有什么副作用,哪些表达式没有意义。这一目标已经达成,所依靠的只是导言声明为已知的内容:第 2 单元的类型、它们的取值范围和它们的转换。导言用来提出问题的那个含六个问题的程序,现在可以像编译器那样去读,并逐个表达式地加以修正。
算术运算
算术运算一课确立了五个算术运算符的确切行为。整数除法向零截断,并且只要商有意义,就满足恒等式 (a / b) * b + a % b == a,因此余数不为零时带有被除数的符号;所以 -7 / 2 等于 ,-7 % 2 等于 ,判断奇数要用 n % 2 != 0。这一课把它同欧几里得余数 ((a % b) + b) % b(适用于 )联系起来,并用二者来分解数量。它教人按照优先级和结合性画出表达式树,并在每个节点上按常用算术转换确定类型。最后讲了没有意义的商(整数除以零,以及最小的整数除以 )和中间结果溢出:即使最终结果放得下,中间结果溢出也足以构成未定义行为。
关系与逻辑
关系与逻辑一课说明,条件是一个 bool 类型的表达式,在树中有它的位置:关系运算符的结合比算术松,&& 比 || 紧,! 比它们都紧。它教人把陈述翻译成条件,并识别那些能编译、意思却不同的条件,例如 1 < x < 5 和 day == 6 || 7。它说明 && 和 || 只有在第一个操作数不能决定结果时才对第二个操作数求值,而这种短路使人能够用左边的守卫来保护一个运算。最后讲了德摩根律,它对复合条件取反,也讲了它的界限:遇到非数的值时,!(a < b) 并不等价于 a >= b。
赋值、副作用与未定义行为
赋值、副作用与未定义行为一课区分了表达式的值和它的作用:赋值是一个表达式,它的值是所赋的值,并且改变一个变量;x++ 的值是原来的值,而 ++x 的值是新值。它说明表达式树并不规定各分支的计算顺序,因此对同一变量的两个作用,或者一个作用和一次读取,如果彼此之间没有顺序,就会使表达式失去意义;它还准确说明了 C++17 规定了哪些顺序,又留下了哪些未指明。它汇总了四个等级的目录(良定义、实现定义、未指明和未定义),解释了编译器在推理时假定程序没有未定义行为,并针对目录中的每一种情形说明了哪种工具能发现它。
位与数学库
位与数学库一课说明,整数也是一串位:&、| 和 ^ 对每一列施加一个联结词,~ 把所有位取反,移位则乘以或除以 2 的幂,条件是移位的位数既不为负,也不达到类型的宽度。用这些运算符,它构造了查询、置位、清零和翻转某一位的掩码。它提醒说,这些运算符的结合比关系运算符松,因此 x & 1 == 0 问的并不是看上去的问题,而且 ^ 不是乘方。最后讲了库:<cmath>、<numbers>、<numeric> 和 <bit> 提供正确且经过检验的计算,各有自己的前置条件,例如绝对值的前置条件:最小的整数没有绝对值。
正在加载内容…
已完成
登录账户以保存您的进度