Ada 高可靠性编程语言

FreeGuideOnline 37阅读 2026-07-11

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_LineAda.Text_IO 包中的过程,用于输出字符串并换行。

Ada 不允许存在游离的语句;所有代码都在声明式结构内,这强制了良好的结构设计。

基础语法核心要素

标识符与保留字

Ada 标识符不区分大小写(Hellohello 是同一个)。保留字完整易读,如: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_ErrorStorage_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;