本课目标
完成本课后,你将能够:
-
理解Ada类型系统的核心哲学与设计原则
-
掌握标量类型与复合类型的完整分类体系
-
区分类型声明、子类型、派生类型的本质差异
-
建立Ada类型系统的整体认知框架
-
应用类型安全原则编写高可靠性代码
一、Ada类型系统的哲学
1.1 "强类型"的真正含义
Ada的类型系统遵循一个核心原则:让类型错误在编译期被发现,而非运行时崩溃。
-- C语言风格(弱类型):编译通过,运行可能出错
int distance = 100;
int temperature = 20;
int result = distance + temperature; // 无意义但合法!
-- Ada风格(强类型):编译期拦截错误
type Distance is range 0 .. 100_000; -- 距离类型
type Temperature is range -273 .. 5000; -- 温度类型
D : Distance := 100;
T : Temperature := 20;
Result : Distance := D + T; -- ERROR! 不同类型不能运算
强类型的价值:
| 场景 | 弱类型语言 | Ada 强类型 |
|---|---|---|
| 单位混淆 | 运行时发现 | 编译期报错 |
| 范围溢出 | 静默回绕或崩溃 | 运行时检查(可关闭) |
| 类型转换 | 隐式自动转换 | 必须显式转换 |
| 接口契约 | 文档约定 | 编译器强制执行 |
经典案例:1999年火星气候轨道器坠毁,因英制与公制单位混淆。而类型系统可避免此类错误。
1.2 类型 vs 子类型 vs 派生类型
| 概念 | 语法 | 本质 | 关系 |
|---|---|---|---|
| 类型(Type) | type T is ... | 全新、不兼容的类型 | 与父类型无关 |
| 子类型(Subtype) | subtype S is T range ... | 父类型的子集,完全兼容 | S 是 T 的子集 |
| 派生类型(Derived) | type D is new T; | 继承父类型操作,但不兼容 | D 是 T 的"副本" |
-- 类型:全新的、不兼容的类型
type Meters is range 0 .. 10_000;
type Feet is range 0 .. 30_000;
M : Meters := 100;
F : Feet := 100;
-- M := F; -- ERROR! 不同类型,即使范围兼容也不能赋值
-- 子类型:兼容父类型的子集
subtype Positive_Meters is Meters range 1 .. Meters'Last;
PM : Positive_Meters := 50;
M := PM; -- OK! 子类型可赋值给父类型
-- 派生类型:继承操作但不兼容
type Kilometers is new Meters; -- 继承所有操作
K : Kilometers := 1;
-- M := K; -- ERROR! 派生类型与父类型不兼容
二、类型分类体系
Ada类型系统的完整分类:
Ada类型
├── 标量类型(Scalar)
│ ├── 离散类型(Discrete)
│ │ ├── 枚举类型(Enumeration)
│ │ │ ├── 布尔类型(Boolean)
│ │ │ ├── 字符类型(Character)
│ │ │ └── 用户定义枚举
│ │ └── 整数类型(Integer)
│ │ ├── 预定义整数(Integer等)
│ │ └── 用户定义整数
│ └── 实数类型(Real)
│ ├── 浮点类型(Floating-Point)
│ └── 定点类型(Fixed-Point)
├── 复合类型(Composite)
│ ├── 数组类型(Array)
│ │ ├── 约束数组(Constrained)
│ │ └── 非约束数组(Unconstrained)
│ ├── 记录类型(Record)
│ │ ├── 简单记录
│ │ ├── 变体记录(Variant)
│ │ └── 标记记录(Tagged)
│ └── 任务/保护类型(Task/Protected)
├── 访问类型(Access)← 指针
└── 私有类型(Private)← 信息隐藏
三、标量类型详解
3.1 离散类型(Discrete)
离散类型的特点:值可枚举,有明确的前驱和后继。
枚举类型
-- 用户定义枚举
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
type Color is (Red, Orange, Yellow, Green, Blue, Indigo, Violet);
-- 枚举属性
D : Day := Mon;
Next_D : Day := Day'Succ (D); -- Tue
Pos : Integer := Day'Pos (Wed); -- 2(从0开始)
-- 遍历枚举
for D in Day loop
Process (D);
end loop;
预定义枚举类型:
| 类型 | 定义 | 用途 |
|---|---|---|
Boolean | (False, True) | 逻辑值 |
Character | ASCII/Latin‑1 字符 | 8 位字符 |
Wide_Character | Unicode BMP | 16 位字符 |
Wide_Wide_Character | 全 Unicode | 32 位字符 |
整数类型
-- 预定义整数类型
type Integer is range implementation_defined; -- 通常32位
type Natural is range 0 .. Integer'Last; -- 非负整数
type Positive is range 1 .. Integer'Last; -- 正整数
-- 用户定义整数(推荐!)
type Byte is range -128 .. 127; -- 8位有符号
type Word is range 0 .. 65_535; -- 16位无符号
type Long is range -(2**63) .. 2**63 - 1; -- 64位有符号
-- 模类型(自动回绕,无溢出检查)
type Byte_Mod is mod 256; -- 0 .. 255,255+1=0
type Bit_Field is mod 2**32;
整数类型属性:
| 属性 | 含义 | 示例 |
|---|---|---|
'First | 最小值 | Integer'First = -2^31 |
'Last | 最大值 | Integer'Last = 2^31-1 |
'Range | 范围 | Byte'Range = -128 .. 127 |
'Size | 存储位数 | Byte'Size = 8 |
'Image | 字符串表示 | 42'Image = " 42" |
'Value | 解析字符串 | Integer'Value ("42") = 42 |
3.2 实数类型(Real)
实数类型表示连续值,无前驱/后继概念。
浮点类型
-- 预定义浮点
type Float is digits implementation_defined; -- 通常6位精度
type Long_Float is digits 15; -- 扩展精度
-- 用户定义(指定十进制精度)
type Real is digits 6; -- 至少6位十进制精度
type Double is digits 15; -- 至少15位精度(IEEE双精度)
-- 指定范围
type Probability is digits 6 range 0.0 .. 1.0;
浮点属性:
| 属性 | 含义 |
|---|---|
'Digits | 十进制精度位数 |
'Machine_Mantissa | 二进制尾数位数 |
'Machine_Emin | 最小指数 |
'Machine_Emax | 最大指数 |
'Epsilon | 与 1.0 的最小差值 |
'Safe_First | 安全范围最小值 |
'Safe_Last | 安全范围最大值 |
定点类型(嵌入式关键)
定点类型用整数运算模拟小数,无硬件浮点单元时的最佳选择:
-- 普通定点(二进制缩放)
type Fixed is delta 0.01 range -100.0 .. 100.0;
-- delta指定最小精度,范围必须对称于0
-- 小数定点(十进制,适合货币)
type Money is delta 0.01 digits 10;
-- 共10位十进制,小数点后2位(由delta决定)
-- 特定精度定点
type Angle is delta 2.0**(-15) range -Pi .. Pi;
-- delta = 1/32768,适合16位整数存储
定点 vs 浮点:
| 特性 | 定点 | 浮点 |
|---|---|---|
| 运算速度 | 快(整数运算) | 较慢(需FPU或软件模拟) |
| 精度 | 绝对精度固定 | 相对精度固定 |
| 范围 | 编译期确定 | 动态范围大 |
| 硬件依赖 | 低(纯整数运算) | 高(需FPU支持) |
| 适用场景 | 嵌入式、控制、货币 | 科学计算、图形 |
四、复合类型详解
4.1 数组类型
-- 约束数组(编译期确定边界)
type Vector is array (1 .. 10) of Float;
type Matrix is array (1 .. 3, 1 .. 3) of Float;
-- 非约束数组(声明时指定边界)
type Int_Array is array (Integer range <>) of Integer;
-- 使用时:A : Int_Array (1 .. 100);
-- 索引可为任意离散类型
type Week_Temps is array (Day) of Float; -- 以Day枚举为索引
Temps : Week_Temps;
Avg := (Temps (Mon) + Temps (Tue)) / 2.0;
数组属性:
| 属性 | 含义 |
|---|---|
'First / 'Last | 索引范围 |
'Range | 索引范围(用于for循环) |
'Length | 元素个数 |
'Component_Size | 元素存储位数 |
4.2 记录类型
-- 简单记录
type Point is record
X, Y : Float;
end record;
-- 变体记录(类似C的union,但类型安全)
type Shape_Kind is (Circle, Rectangle, Triangle);
type Shape (Kind : Shape_Kind) is record
case Kind is
when Circle =>
Radius : Float;
when Rectangle =>
Width, Height : Float;
when Triangle =>
A, B, C : Float;
end case;
end record;
-- 使用
S : Shape (Kind => Circle); -- 必须指定判别式
S.Radius := 5.0;
-- S.Width := 10.0; -- ERROR! 当前变体无Width字段
五、访问类型(指针)
-- 命名访问类型
type Int_Ptr is access Integer;
-- 使用
P : Int_Ptr := new Integer'(42); -- 堆分配
P.all := 100; -- 解引用
X : Integer := P.all; -- 读取值
-- 访问常量
type Const_Int_Ptr is access constant Integer;
-- 访问子程序(函数指针)
type Math_Func is access function (X : Float) return Float;
-- 匿名访问类型(Ada 2005+)
procedure Process (Data : access Integer);
Ada指针的安全性:
-
默认非空(
null需显式声明) -
作用域访问类型(防止悬空指针)
-
垃圾回收可选,可显式释放(
Ada.Unchecked_Deallocation)
六、类型安全实践
6.1 物理单位建模
package Physical_Units is
type Meters is new Float;
type Seconds is new Float;
type Meters_Per_Second is new Float;
-- 操作符重载确保单位正确
function "/" (D : Meters; T : Seconds) return Meters_Per_Second;
-- 禁止无意义运算(不定义则自动禁止)
-- function "+" (M : Meters; S : Seconds) return ...; -- 故意不定义!
end Physical_Units;
-- 使用
Distance : Meters := 100.0;
Time : Seconds := 10.0;
Speed : Meters_Per_Second := Distance / Time; -- OK!
-- Bad := Distance + Time; -- ERROR! 无此运算
6.2 状态机建模
type State is (Idle, Heating, Cooling, Error);
procedure Control_Loop is
Current_State : State := Idle;
begin
loop
case Current_State is
when Idle =>
if Temperature < Setpoint - 2.0 then
Current_State := Heating;
elsif Temperature > Setpoint + 2.0 then
Current_State := Cooling;
end if;
when Heating =>
Turn_On_Heater;
if Temperature >= Setpoint then
Current_State := Idle;
elsif Sensor_Fault then
Current_State := Error;
end if;
when others =>
Handle_Error;
end case;
end loop;
end Control_Loop;
七、本课总结
-
Ada类型系统核心:编译期发现错误,强制物理意义与逻辑正确性
-
类型全新不兼容,子类型兼容父类型,派生类型继承操作但不兼容
-
标量类型:离散(枚举、整数)有前后继,实数(浮点、定点)连续
-
复合类型:数组(同构集合)、记录(异构聚合)、变体记录(类型安全union)
-
访问类型:安全指针,默认非空,支持作用域管理与子程序访问
八、课后练习
1.类型设计:为"日期"设计类型体系,包括年、月、日,确保2月不会有30日。
2.单位安全:扩展Physical_Units,添加加速度(m/s²)和力(N = kg·m/s²)。
3.枚举遍历:编写程序输出一周七天及其对应的数字(Mon=1, Tue=2...)。
4.定点计算:用定点类型计算圆的面积,比较与浮点的精度差异。
5.变体记录:设计一个"消息"类型,可以是文本、数字或错误代码。
九、下节预告
第9课|整型类型详解
我们将:
-
深入预定义整数类型与机器整数类型
-
掌握用户定义整数的范围与精度控制
-
理解模类型的回绕特性与应用场景
-
学习整数运算的溢出检查与饱和处理
关键术语表
标量类型:单值类型,包括离散类型和实数类型
离散类型:值可枚举的类型,有明确前驱后继
派生类型:继承父类型操作但与之不兼容的新类型
子类型:父类型的子集,与父类型完全兼容
变体记录:根据判别式动态改变字段的记录类型
定点类型:用整数运算模拟小数的实数类型