第8课|数据类型概述

370 阅读8分钟

本课目标

完成本课后,你将能够:

  • 理解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 ...父类型的子集,完全兼容ST 的子集
派生类型(Derived)type D is new T;继承父类型操作,但不兼容DT 的"副本"
-- 类型:全新的、不兼容的类型
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)逻辑值
CharacterASCII/Latin‑1 字符8 位字符
Wide_CharacterUnicode BMP16 位字符
Wide_Wide_Character全 Unicode32 位字符

整数类型

-- 预定义整数类型
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课|整型类型详解

我们将:

  • 深入预定义整数类型与机器整数类型

  • 掌握用户定义整数的范围与精度控制

  • 理解模类型的回绕特性与应用场景

  • 学习整数运算的溢出检查与饱和处理


关键术语表

标量类型:单值类型,包括离散类型和实数类型

离散类型:值可枚举的类型,有明确前驱后继

派生类型:继承父类型操作但与之不兼容的新类型

子类型:父类型的子集,与父类型完全兼容

变体记录:根据判别式动态改变字段的记录类型

定点类型:用整数运算模拟小数的实数类型