| Candidates should be able to: | Notes and guidance |
|---|---|
| Show understanding of why user-defined types are necessary | |
| Define and use non-composite types | Including enumerated, pointer |
| Define and use composite data types | Including set, record and class/object |
| Choose and design an appropriate user-defined data type for a given problem |
数据表示
A-Level 计算机科学 · 第 13 主题
13.1
用户自定义数据类型
大纲
来源:剑桥国际大纲
内建类型(INTEGER、REAL、STRING、CHAR、BOOLEAN)覆盖最简单的情况。对于更丰富的问题,你可以定义用户定义类型(user-defined types),使代码更清晰、编译器更严格。
Why they are needed
一个内建的 STRING 让你在一个应当只容纳几个合法值之一的字段里存储乱七八糟的东西;一个用户定义类型可以限制它。真实的实体通常是不同类型的值的一个集合。而 DECLARE Taxi : Vehicle 比 DECLARE Taxi : STRING 更清晰(自我说明)。
Non-composite types
Enumerated type
一个枚举类型(enumerated type)的值是一个命名常量的固定列表:
TYPE Vehicle = (M100, M230, T101, T102, T120, T150)
DECLARE MyTaxi : Vehicle
MyTaxi ← T102
这些名称是新类型的值(内部存储为小整数);你不能赋任何列表之外的东西。用途:星期几、颜色、状态码。

Pointer type
一个指针(pointer)容纳另一个变量的内存地址(或 NULL 表示"没有目标")。指针构建动态结构(链表、树)并传递引用而不复制。
TYPE PNode = ^TNode // pointer to a TNode
DECLARE p : PNode
p ← NEW TNode
p^.Value ← 42 // dereference to reach the fields
解引用(dereference,p^)意味着访问它指向的变量。

p^ 解引用它以访问该节点的字段Composite types
一个复合类型(composite type,复合数据类型之一)把几个值组合在一个名称下。


- record(记录,主题 10)——一个
TYPE ... ENDTYPE块中不同类型的字段。 - set(集合)——一个唯一值的无序集合,带操作:添加、移除、成员测试、并集、交集:
DECLARE Available : SET OF Colour
Available ← {Red, Blue}
IF Green IN Available THEN ...
- class(类)/ object(对象)——OOP 复合类型,把数据字段(属性(attributes))与对它们的操作(方法(methods))结合。一个对象是一个类的一个实例:
CLASS Taxi
PRIVATE Capacity : INTEGER
PUBLIC FUNCTION GetCapacity() RETURNS INTEGER
RETURN Capacity
ENDFUNCTION
ENDCLASS
Choosing a type
对来自一个固定列表的值用枚举,为间接性用指针,为一组字段用记录,为一个无序的唯一集合用集合,而当你需要状态和行为在一起时用类。
Programming concept lab
Connect examples to the programming idea they show.
| 英文 | 中文 | 拼音 |
|---|---|---|
| user-defined type | 用户定义类型 | yòng hù dìng yì lèi xíng |
| enumerated type | 枚举类型 | méi jǔ lèi xíng |
| pointer | 指针 | zhǐ zhēn |
| dereference | 解引用 | jiě yǐn yòng |
| composite type | 复合类型 | fù hé lèi xíng |
| record | 记录 | jì lù |
| set | 集合 | jí hé |
| class | 类 | lèi |
| object | 对象 | duì xiàng |
| attributes | 属性 | shǔ xìng |
| methods | 方法 | fāng fǎ |
13.2
文件组织与访问
大纲
| Candidates should be able to: | Notes and guidance |
|---|---|
| Show understanding of the methods of file organisation and select an appropriate method of file organisation and file access for a given problem | Including serial, sequential (using a key field), random (using a record key) |
| Show understanding of methods of file access | Including Sequential access for serial and sequential files Direct access for sequential and random files |
| Show understanding of hashing algorithms | Describe and use different hashing algorithms to read from and write data to a random/sequential file |
来源:剑桥国际大纲
文件组织(file organisation)是数据如何布置;文件存取(file access)是程序如何访问一条记录。
- serial file(串行文件)——记录按添加的顺序,没有排序。存取只能是顺序的;追加很快;搜索很慢。用于日志和审计追踪。
- sequential file(顺序文件)——记录按一个键排序。搜索更快(你可以尽早停止或二分查找);插入很慢(记录必须移位)。用于批量更新的主文件。
- random (direct-access) file(随机文件)——记录在从键算出的位置(常用一个散列)。按键的直接存取非常快;按键顺序读取更难。用于大的查找表和客户账户。



两种存取方法是顺序存取(sequential access,从头读到尾)和直接存取(direct access,直接跳到一个已知位置)。把结构匹配到主导操作:单键查找偏向随机;按序报表偏向顺序。
File access route
Follow a file from storage to program and back safely.
| 英文 | 中文 | 拼音 |
|---|---|---|
| file organisation | 文件组织 | wén jiàn zǔ zhī |
| serial file | 串行文件 | chuàn xíng wén jiàn |
| sequential file | 顺序文件 | shùn xù wén jiàn |
| random (direct-access) file | 随机文件 | suí jī wén jiàn |
| sequential access | 顺序存取 | shùn xù cún qǔ |
| direct access | 直接存取 | zhí jiē cún qǔ |
13.2
散列
一个散列函数(hash function,一个散列算法)取一个记录键并产生一个存储记录的地址(address)。一个好的散列函数快、确定性(deterministic),并把键均匀地铺开。
$N$ 个槽的常见散列算法:取模散列 address ← key MOD N;折叠(把键拆开、把各部分相加、MOD N);一个字符串散列(把字符码相加、MOD N)。
一个冲突(collision)是当两个键散列到同一个地址时。解决它的三种方式:
| 策略 | 它如何工作 | 权衡 |
|---|---|---|
| 线性探测(linear probing) | 用下一个空闲槽(绕回) | 简单,但键聚集 |
| 链接法(chaining) | 每个槽指向一个记录的链表(linked list) | 无聚集,但用更多内存 |
| 再散列 | 应用第二个散列函数 | 铺开键,但更多工作 |

要搜索:散列键,读那个槽;若键匹配你就完成了,否则跟随解决策略直到一个匹配或一个空槽。要插入:散列键,写入那个槽或下一个空闲的。保持装填因子(load factor,记录 ÷ 槽)低于约 70% 以获得接近 O(1) 的查找。
A hash table
Watch each key get hashed to a bucket. A good hash spreads keys out so lookups stay fast.
| 英文 | 中文 | 拼音 |
|---|---|---|
| deterministic | 确定性 | què dìng xìng |
| hash function | 散列函数 | sàn liè hán shù |
| collision | 冲突 | chōng tū |
| linear probing | 线性探测 | xiàn xìng tàn cè |
| chaining | 链接法 | liàn jiē fǎ |
| linked list | 链表 | liàn biǎo |
| load factor | 装填因子 | zhuāng tián yīn zi |
13.3
浮点数的表示与运算
大纲
| Candidates should be able to: | Notes and guidance |
|---|---|
| Describe the format of binary floating-point real numbers | Use two's complement form Understand of the effects of changing the allocation of bits to mantissa and exponent in a floating-point representation |
| Convert binary floating-point real numbers into denary and vice versa | |
| Normalise floating-point numbers | Understand the reasons for normalisation |
| Show understanding of the consequences of a binary representation only being an approximation to the real number it represents (in certain cases) | Understand how underflow and overflow can occur |
| Show understanding that binary representations can give rise to rounding errors |
来源:剑桥国际大纲
为了存储大小非常不同的实数,计算机用一个浮点(floating-point)格式——科学记数法的一个二进制形式,带两个字段:
- 一个尾数(mantissa)——有效数字。
- 一个指数(exponent)——要乘的 2 的幂。
两者都存储为补码(two's complement)整数。值是
把尾数读作一个二进制分数——小数点后第一位值 $1/2$,下一位 $1/4$,然后 $1/8$,如此等等。所以 0.1010000 是 $1/2 + 1/8 = 0.625$;带指数 00000010(= 2)值是 $0.625 \times 2^{2} = 2.5$。

Converting
- 二进制 → 十进制:把尾数(若为负用补码规则)读作一个分数,把指数读作一个有符号整数,然后把尾数乘以 $2^{\text{exponent}}$。
- 十进制 → 二进制:把数写成一个二进制分数 × 一个 2 的幂,然后把尾数和指数存储为约定的格式。
例题。 一个数有尾数 10110000 和指数 00000011。求它的十进制值。
指数 00000011 是 $+3$。尾数以一个 1 开始,所以它是负的。在补码中读作 1.0110000,符号位值 $-1$,分数位加上 $\tfrac{1}{4} + \tfrac{1}{8} = 0.375$,所以尾数是 $-1 + 0.375 = -0.625$。于是
例题。 在这个格式中存储 $+2.5$。
在二进制中 $2.5 = 10.1$。写成一个规格化的分数,$2.5 = 0.101 \times 2^{2}$。所以尾数是 01010000(符号位 0,然后 .101)而指数是 00000010($= 2$)。
Normalisation
一个数在它的第一个有效位紧跟在二进制小数点之后时是规格化的(normalised,没有浪费的前导零)。这最大化精度,因为每个尾数位都携带信息。要规格化,把尾数左移并减小指数(或右移并增大它),直到第一个有效位就位;值不变。对负(补码)尾数,符号位(1)之后紧跟一个 0。

Approximation and rounding errors
许多十进制实数不能在二进制中被精确存储——例如 $0.1_{10}$ 是循环二进制分数 $0.000110011\ldots_{2}$,它必须被截断。后果:
- 舍入误差(rounding errors)在许多操作上累积(
0.1 + 0.2不精确地是0.3)。 - 比较失败——测试
ABS(x - 0.3) < 1e-9而不是x = 0.3。 - 两个几乎相等的值相减会损失精度。
- 当指数用尽范围时,溢出(overflow,一个对指数范围太大的结果)和下溢(underflow,一个太小的结果,舍入为零)发生。
对于精确的需要(货币),用定点(fixed-point)或 BCD(二进码十进数)而不是浮点。
Build a floating-point number
Flip the mantissa and exponent bits to make a value, and check whether it is normalised.
Normalising a floating-point number
Step through normalisation. Shifting the mantissa to remove wasted leading zeros — and adjusting the exponent to match — keeps the value the same but spends every bit on precision.
| 英文 | 中文 | 拼音 |
|---|---|---|
| floating-point | 浮点 | fú diǎn |
| mantissa | 尾数 | wěi shù |
| exponent | 指数 | zhǐ shù |
| two's complement | 补码 | bǔ mǎ |
| normalised | 规格化 | guī gé huà |
| rounding errors | 舍入误差 | shě rù wù chā |
| fixed-point | 定点 | dìng diǎn |
| BCD | 二进码十进数 | èr jìn mǎ shí jìn shù |
| overflow | 溢出 | yì chū |
| underflow | 下溢 | xià yì |
13.3
考试技巧
- 对于浮点二进制 → 十进制,把尾数读作一个分数、把指数读作一个有符号整数,然后相乘;一个负尾数遵循补码规则。
- 通过移位直到小数点后第一位与符号位不同来规格化——这最大化精度。
- 解释舍入、溢出和下溢误差以及为什么 $0.1$ 不能被精确存储。
- 比较文件组织(串行、顺序、直接)以及散列如何快速找到一条记录。
本主题的互动课程
逐步学习,并即时检测练习。