摘要
本课开启本课程的第三单元,这一单元专讲表达式及其运算符。本课说明这一单元的用途:教人构造其值能够被确定无疑地预测的表达式,知道哪个运算先做、结果是什么类型、有哪些副作用,以及哪些表达式没有意义;介绍本单元的四节课和贯穿它们的线索,从整数和实数的算术一直到位和数学库,中间经过条件和未定义行为的目录;回顾本单元假定已从第二单元掌握的内容,即每个数据的类型、它的界限和它的转换;提出本单元要解决的问题,即一个编译时没有任何警告的程序,它对六个学校里的问题给出的回答全都是错的,甚至给不出回答;并说明本单元通向何处,包括后面各单元以及“C++ 程序设计 II”课程。
学习目标
- 描述本单元内容的顺序,并说明每节课为其后的那节课提供了什么。
- 回顾第 2 单元中必要的内容(整数类型和实数类型、它们的取值范围、溢出以及转换),必要时加以复习。
- 借助一个例子预先认识到:表达式的值取决于求值规则(优先级、操作数的类型、作用的顺序),而这些规则并不总是与学校数学的规则一致。
- 认识学完本单元时应当会做什么,并把它与后面各单元将要学习的内容区分开。
入门测评
在学习本单元之前,先测一测自己的起点是有益的。这项测评是自我测评:不计分,唯一的目的是为学习指引方向。前四道题检查本单元假定已从第二单元掌握的内容:整数之间的除法、二进制写法、无符号减法以及有符号溢出。其后的六道题按课的顺序涉及本单元的核心思想,其中有几道从学校数学或初等逻辑出发。没有人必须现在就答对,其中两道题里,“我还不知道”是诚实的回答:每个答案,无论对错,都会解释要点,并指出讲解这一要点的那节课。同样的能力会在单元末尾的总结课中再次测量。
开始本单元之前,先测一测你的起点。这是一次简短的自我测评:不计分,结果只保存在你的浏览器中。
第 1 题来自前面的单元一个程序声明
const double r{7 / 2};并输出r。会显示什么?以为变量的类型决定如何做除法;决定它的是操作数的类型。以为整数除法会舍入;实际上它舍去小数部分。值 3 是常量,可以在double中精确表示:不存在需要拒绝的窄化。除法7 / 2在整数之间进行,得到整数商 3;之后这个 3 才被转换为double。决定运算的是操作数的类型,而不是接收结果的变量的类型。第 3.1 课把这一观察变成一条一般规则:类型在表达式的每个节点上决定。复习: 字符、布尔值、类型转换与常量
第 2 题来自前面的单元用八位二进制怎样写出数 13?
权加错了:。按得出的顺序写下连续除法的余数;它们应当从最后一个读到第一个。这个位模式是 26,即 13 的两倍:各位错移了一个位置。每一位的权是其右边一位的两倍:各个 1 位于权为 8、4 和 1 的位置。第 3.4 课直接在这些位上运算,所以最好能熟练地读出它们。复习: 整数及其表示
第 3 题来自前面的单元对于 32 位的
unsigned int,std::println("{}", 0u - 1);输出什么?没有考虑后缀u:无符号类型没有负值。把在模 意义下有定义的无符号算术与有符号溢出混为一谈。以为减法会停在该类型的最小值上;实际上它会绕回。无符号算术是模 运算:。结果是有定义的,所以没有任何检测器会报告它。本单元会在混合表达式的转换中以及在位运算符中再次遇到它,位运算符要用无符号类型来写。复习: 整数及其表示
第 4 题来自前面的单元一个程序先执行
int count{std::numeric_limits<int>::max()};,再执行count = count + 1;。关于count的值,标准保证什么?把有时观察到的现象与标准所保证的混为一谈;绕回到最小值是无符号类型的行为,不是有符号类型的行为。忽视了类型的界限:这个数放不进 32 位的int。编译器不知道程序运行时会有哪些值;它几乎总是一声不响地完成编译。加法有一个前置条件,即结果能放进该类型,超出这一条件,标准就不作任何承诺。本课程的检测器会终止程序;没有它,程序可能输出任何东西。第 3.3 课把这种情形与其他同类情形一起收入一份目录。复习: 整数及其表示
第 5 题 和 各等于多少?
从左到右依次运算,没有顾及乘法的优先级。忽视了第二个表达式中的括号。把每个表达式的值安到了另一个头上:把括号的作用弄反了。乘法比加法结合得更紧:在 中先做乘法,而括号会改变这个顺序。C++ 遵循同样的约定,第 3.1 课把它推广到除法、取余和结合性,并以表达式树为工具。复习: 算术运算
第 6 题根据数学中的除法定理,在余数 受条件 约束时,商 和余数 ( 除以 时)各是多少?
这是 C++ 算出的结果,但不满足 :数学中的余数不是负数。没有验证恒等式:,而不是 。同样不满足恒等式:。定理要求 ,并且 ,而只有这两个数满足它。C++ 则把商向零截断:-7 / 2等于 ,-7 % 2等于 。第 3.1 课解释这一差别,并教人如何从 C++ 的余数得到数学中的余数。复习: 算术运算
第 7 题“下雨并且冷”的否定是什么?
对每一部分取否定却没有改变联结词:这断言的东西比否定多得多,因为它连不下雨而冷的日子也排除了。只否定了其中一部分。与第二个选项说的是同一件事,错误也相同。合取的否定是否定的析取:这是德摩根律之一。只要两件事中缺了一件,这个断言就为假。第 3.2 课把同一条定律用于程序的条件。复习: 关系与逻辑
第 8 题
std::println("{}", 3 < 5);输出什么?以为逻辑值会写成数字;使用占位符{}时,std::println写出的是单词。以为表达式会被当作文字写出;写出的是它的值。它是值:比较是bool类型的表达式。比较和其他表达式一样是表达式:它有一个bool类型的值,std::println把它写成true或false。第 3.2 课从这一观察出发:条件是被计算出来的,因此它有优先级、有树、有代数。复习: 关系与逻辑
第 9 题执行
int a{5};和int b{a++ * 2};之后,a和b各等于多少?这是诚实的回答:第 3.3 课讲授表达式的值与它的作用之间的区别,结业测评还会再问。忘记了作用:a++会使a增加。用了a的新值;后缀自增给出的是原来的值。表达式a++有一个值,即a原来的值(5),还有一个作用,即把a变成 6。乘法用的是值,不是作用。第 3.3 课教人把这两者分开,并识别出同一个表达式中的两个作用何时会使它失去意义。复习: 赋值、副作用与未定义行为
第 10 题
std::println("{}", 2 ^ 3);输出什么?这是诚实的回答:第 3.4 课会解释它,结业测评还会再问。把脱字符读成乘方,就像许多计算器的记法那样;在 C++ 中它不是。把它读成乘积。在 C++ 中,脱字符不是乘方,而是按位异或: 和 只在权为 1 的位上不同,所以结果是 1。C++ 没有乘方运算符;第 3.4 课讲授何时使用std::pow,何时使用移位。复习: 位与数学库
正在加载内容…
已完成
登录账户以保存您的进度