摘要
本课开启本课程的第二单元,这一单元专讲数据及其类型。本课说明这一单元的用途:讲明程序中的每个数据都带着一个类型存放在内存里,类型规定它能取哪些值、占多少空间、允许哪些运算,并且这些限制会带来可以观察到的后果;介绍本单元的四节课和贯穿它们的线索,从作为带有名字和值的存储单元的变量,一直到类型之间的转换和常量;回顾本单元假定已从第一单元掌握的内容,即编写、编译并运行一个读取输入、写出结果的程序;提出本单元要解决的问题,即一个看上去正确的程序,其计算结果却与数学的结果不一致,从和 说起,它在计算机中并不恰好等于 ;最后指出它通向何处,即后面的各单元和“C++ 程序设计 II”课程。
学习目标
- 描述本单元内容的顺序,并说明每节课为其后的那节课提供了什么。
- 回顾第 1 单元中必要的内容(编译与运行的流程、带格式的输出、用
std::cin读取,以及阅读编译器的消息),必要时加以复习。 - 借助一个例子,解释为什么程序中的数不是数学中的数,并预先说明为什么在计算机中 不恰好等于 。
- 认识学完本单元时应当会做什么,并把它与后面各单元将要学习的内容区分开。
入门测评
在学习本单元之前,先测一测自己的起点是有益的。这项测评是自我测评:不计分,唯一的目的是为学习指引方向。前四道题检查本单元假定已从第一单元掌握的内容:逐步跟踪一个过程、用两位小数写出一个数、舍入,以及阅读一条错误消息。其后的六道题按课的顺序涉及本单元的核心思想。没有人必须现在就答对,其中一道题里,“我还不知道”是诚实的回答:每个答案,无论对错,都会解释要点,并指出讲解这一要点的那节课。同样的能力会在单元末尾的总结课中再次测量。
开始本单元之前,先测一测你的起点。这是一次简短的自我测评:不计分,结果只保存在你的浏览器中。
第 1 题来自前面的单元一个过程写道:“1. 取值 17。2. 取值 。3. 取值 。”结束时 的值是多少?
以为每一步都从初始值出发;第二步是从第一步留下的值出发。把“取值”读成了数学等式;它其实是一条替换 的值的命令。把变量与常量混为一谈:每次给变量赋另一个值,它的值就会改变。每一步都从 在那一刻的值出发,而不是从初始值出发。手工执行这个过程,并记下每一步之后每个变量的值,就是第 1 单元的跟踪表;第 2.1 课把它变成推理程序状态的工具。复习: 从问题到算法
第 2 题来自前面的单元语句
std::println("{:.2f}", 2.0 / 3);输出什么?以为格式说明会截断;它会舍入到最接近的小数。忽略了格式说明:这是不带.2f时的写法。以为这是整数除法;有了带小数的2.0,除法就是实数除法。格式说明.2f以两位小数输出这个数,并进行舍入: 输出为0.67。存储的值不变,变的只是它的写法。复习: 输入、输出与错误
第 3 题来自前面的单元要把 写成两位小数,并进行舍入。得到什么?
截断而不是舍入:不看第三位就把它丢掉了。舍入到了一位小数,而不是两位。把第二位小数加得过多: 位于 与 之间,舍入只在这两个数中选一个。舍入到两位小数,就是选出最接近的两位小数: 离 比离 更近。第 2.3 课将说明,计算机也用同样的方式舍入,只不过是在二进制下进行,因此几乎没有哪个带小数的数能被精确存储。复习: 输入、输出与错误
第 4 题来自前面的单元一个程序的第 5 行是
const int pages{12},第 6 行是std::println("{}", pages);。编译器输出6:5: error: expected ',' or ';' before 'std'。缺陷在哪里?第 6 行是正确的;编译器指向它,是因为在那里才察觉到缺失。std是认识的;消息说的是在它前面期望一个分号。消息写的是error,不是warning:没有生成可执行文件。编译器在第 6 行开头遇到一个不能跟在声明后面的东西时,才发现缺了分号,于是指向那个位置。如果被指出的那一行是正确的,缺陷通常在它前一行的末尾。复习: 程序的解剖
第 5 题一个程序声明了
int a{2};和int b{7};,然后执行a = b;和b = 10;。最后a的值是多少?以为赋值把两个变量绑在一起,仿佛a是b的另一个名字;它只复制值。把a = b读反了,仿佛是把a复制到b中;值是从右向左走的。把两个值相加了;赋值是替换,而不是累加。赋值a = b向a复制b在那一刻的值;此后a和b是相互独立的存储单元,改变b不会改变a。复习: 变量与内存
第 6 题数 13 用二进制怎么写?
这是诚实的回答:第 2.2 课讲授如何用二进制写出一个数以及如何读它,结业测评还会再问。按出现的顺序读连续除法的余数;应当从最后一个读到第一个。把权加错了:。在二进制中,每一位的权是它右边那一位的两倍,数字只有 0 和 1。第 2.2 课讲授这一点,因为程序中的每个整数都是这样存储的。复习: 整数及其表示
第 7 题一个 4 字节的
int存放着 ,即它的最大值,程序再给它加 1。可以断言什么?忽略了表示的限制:这个数放不进 4 字节的int。无符号整数才是这样,它们的运算是模运算;对有符号整数,标准并不保证这一点。除非是常量,编译器并不知道程序运行时会有哪些值;它几乎总是一声不响地完成编译。程序中的整数有固定的位数,因而取值范围是有限的。超出有符号整数的取值范围,违反了加法的前置条件;本课程使用的检测器(sanitizer)会让程序停下,没有它,程序可能输出任何东西。第 2.2 课研究这一问题。复习: 整数及其表示
第 8 题 有有限的小数表示吗?
只是近似值:,而不是 1。无论写多少位,总会剩下余数;这个展开是循环的,而不是有限的。这是数和进制的性质,与工具无关;计算器显示的只是近似值。一个既约分数有有限的小数表示,当且仅当它的分母除 2 和 5(即 10 的质因数)之外没有别的质因数。在二进制中条件更严格:分母必须是 2 的幂,所以 没有有限的二进制表示。第 2.3 课由此推出种种后果。复习: 浮点数
第 9 题
std::println("{}", 0.1 + 0.2);输出什么?以为计算机做的是精确的小数加法;它做的是二进制近似值的加法。把默认的写法与格式说明{:.2f}混淆了,后者才舍入到两位小数。两个double相加完全合法;令人吃惊的是它的结果,而不是它的写法。和 都不能在二进制中精确存储,它们的近似值之和也不是 的近似值。std::println输出能唯一确定所存储值的最短十进制表示,而这个表示暴露了差异。第 2.3 课逐位解释它从何而来。复习: 浮点数
第 10 题一个程序声明
const double ratio{7 / 2};,然后输出ratio。会出现什么?以为变量的类型决定怎样做除法;决定它的是操作数的类型。以为整数除法会舍入;它会丢掉小数部分。值 3 是常量,可以在double中精确表示:没有损失,花括号接受它。除法7 / 2是两个整数相除,得到整数商 3;之后这个 3 才被转换为double。做哪种除法,是在运算内部根据操作数的类型决定的,而不是在保存结果的时候。第 2.4 课讲授把转换放在哪里才能得到 3.5。复习: 字符、布尔值、类型转换与常量
正在加载内容…
已完成
登录账户以保存您的进度