Ada 高可靠性编程语言
ada with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is begin Put_Line ("Hello, Safety!"); end Hello;
编译执行:
```bash
gnatmake hello.adb
./hello
代码解析:
with子句引入包(类似 C 的#include),use使包中的内容直接可见。procedure Hello is ... begin ... end Hello;是主程序框架。Put_Line是Ada.Text_IO包中的过程,用于输出字符串并换行。
Ada 不允许存在游离的语句;所有代码都在声明式结构内,这强制了良好的结构设计。
基础语法核心要素
标识符与保留字
Ada 标识符不区分大小写(Hello 与 hello 是同一个)。保留字完整易读,如:begin, end, type, package, task。建议使用下划线分隔风格(如 Sensor_Value),提高可读性。
基本数据类型
Ada 的类型系统是基石,语言鼓励定义自定义数值类型,明确取值范围和精度。
type Temperature is range -40 .. 150; -- 整数类型,上限下限明确
type Voltage is digits 5 range 0.0 .. 5.0; -- 浮点,5位十进制精度
常用预定义类型:Integer, Float, Boolean, Character。但更推荐为具体应用创建约束子类型:
subtype Positive_Count is Integer range 1 .. Integer'Last;
任何试图赋超出范围的值都会在**运行时引发异常(可即时捕获)**或编译时报错(静态分析时)。
变量与常量声明
Count : Integer := 0; -- 变量初始化
Max_Attempts : constant Integer := 3; -- 常量
Sensor_Reading : Temperature; -- 变量未初始化(依赖后续赋值)
使用 constant 创建不可变对象,编译器可据此优化,并防止意外修改。
控制结构
Ada 的控制结构强调整体性,每个分支、循环都必须显式闭合。
条件语句:
if Temperature_Reading > 100 then
Put_Line ("Overheating!");
elsif Temperature_Reading < 0 then
Put_Line ("Freezing!");
else
Put_Line ("Normal operating range.");
end if;
不需要括号包围条件,但 end if 必须出现。
循环语句:
-- 简单循环(无限循环,需显式退出)
loop
exit when Counter = Max;
Counter := Counter + 1;
end loop;
-- for 循环(迭代变量自动声明,只读)
for I in 1 .. 10 loop
Put_Line (Integer'Image (I));
end loop;
数组与记录
数组定义时需指定索引类型和元素类型:
type Sensor_Array is array (1 .. 16) of Temperature;
Readings : Sensor_Array;
支持任意离散类型作为索引,例如枚举索引。
记录(类似结构体):
type Date is record
Year : Integer range 1900 .. 2100;
Month : Integer range 1 .. 12;
Day : Integer range 1 .. 31;
end record;
强大的类型系统与编译时检查
Ada 的类型系统区分命名类型等价,即使两个类型底层表示完全相同,它们也是不同的类型,不允许隐式转换,这称为 强类型(Strong Typing)。
type Meters is new Float;
type Feet is new Float;
M : Meters := 15.0;
F : Feet := 30.0;
-- M := M + F; -- 非法!类型不匹配
M := M + Meters(F); -- 必须显式转换
派生类型(new Float)创建独立类型,而子类型(subtype)只是添加约束,与基类型兼容。这一机制有效防止了单位混淆等灾难性错误。
枚举类型与范围
枚举类型让代码意图更明确:
type Light_State is (Red, Amber, Green);
State : Light_State := Red;
枚举值不能直接与整数混用,避免魔法数字。可以遍历和使用 'Image 属性转为字符串。
属性
Ada 提供大量内置属性查询类型或对象的特性:
Integer'Last– 类型的最大值A'Range– 数组的索引范围I'Image– 将整数值转为字符串Obj'Valid– 检查对象是否处于合法状态(在可能损坏的硬件数据中非常重要)
包:模块化与信息隐藏
Ada 的包(package)是封装逻辑和类型的关键结构,分为规格(spec)和体(body)。规格给出对外可见的接口,体实现具体逻辑。
示例:一个简单的堆栈包
规格文件 stack_pkg.ads:
package Stack_Pkg is
type Stack is limited private; -- 不可赋值/比较的类型
procedure Push (S : in out Stack; Value : Integer);
function Pop (S : in out Stack) return Integer;
function Is_Empty (S : Stack) return Boolean;
private
Max_Size : constant := 100;
type Int_Array is array (1 .. Max_Size) of Integer;
type Stack is record
Data : Int_Array;
Top : Integer range 0 .. Max_Size := 0;
end record;
end Stack_Pkg;
体文件 stack_pkg.adb:
package body Stack_Pkg is
procedure Push (S : in out Stack; Value : Integer) is
begin
S.Top := S.Top + 1;
S.Data (S.Top) := Value;
end Push;
function Pop (S : in out Stack) return Integer is
Value : Integer := S.Data (S.Top);
begin
S.Top := S.Top - 1;
return Value;
end Pop;
function Is_Empty (S : Stack) return Boolean is
begin
return S.Top = 0;
end Is_Empty;
end Stack_Pkg;
limited private 类型禁止赋值和比较操作,必须通过明确提供的子程序操作,完全控制数据的变化——这是安全关键系统中推崇的设计。
异常处理:提前为错误建模
Ada 内置异常处理,且与语言紧密结合。常见预定义异常:Constraint_Error (范围违反)、Program_Error、Storage_Error 等。
declare
type Score is range 0 .. 100;
S : Score;
begin
S := 150; -- 计算溢出会引发 Constraint_Error
exception
when Constraint_Error =>
Put_Line ("Score out of valid range. Resetting.");
S := 0;
end;
最佳实践:在底层子程序内部不处理过多异常,让异常向上传播,在合适的层次进行恢复或安全停机。结合 Ada 的契约(前置/后置条件),许多异常可以被编译期静态预防。
任务与并发:高可靠性并发模型
Ada 的**任务(task)**是内建的一等公民,不需要额外库,就能实现并发与实时调度,这避免了线程库带来的不确定性和复杂交互。
任务通过**入口(entry)**进行同步通信——一种类似客户端-服务器的机制,天然避免竞态条件。
生产者-消费者示例:
with Ada.Text_IO; use Ada.Text_IO;
procedure Producer_Consumer is
task Producer;
task Consumer is
entry Deliver (Item : Integer);
end Consumer;
task body Producer is
begin
for I in 1 .. 10 loop
Consumer.Deliver (I);
end loop;
end Producer;
task body Consumer is
begin
loop
select
accept Deliver (Item : Integer) do
Put_Line ("Consumed: " & Integer'Image (Item));
end Deliver;
or
terminate; -- 当没有更多可接受的入口调用且Producer终止时,自动终止
end select;
end loop;
end Consumer;
begin
null; -- 主程序等待任务完成
end Producer_Consumer;
select 语句支持非确定性选择、超时、终止等待,非常适合实时系统。任务可以设置优先级,与底层实时操作系统(RTOS)调度器协同工作。
契约式编程:前置与后置条件
Ada 2012 引入了契约(Contracts),向函数和过程添加前置条件(Pre)和后置条件(Post),编译器可在运行时检查或静态证明。
function Divide (A, B : Integer) return Integer
with Pre => B /= 0,
Post => Divide'Result * B = A;