第3课|第一个Ada程序:Hello World深入解析

36 阅读7分钟

本课目标

完成本课后,你将能够:

  • 完整理解Ada程序的结构与语法元素

  • 掌握 with 子句的多种用法和作用域规则

  • 正确使用Ada的标识符、保留字和注释规范

  • 区分声明部分与执行部分的职能边界

  • 编写符合工业标准的规范Ada代码


一、程序结构全景解析

1.1 Hello World的完整解剖

回顾上节课的代码:

with Ada.Text_IO;

procedure Hello is
   -- 声明部分(Declarative Part)
begin
   -- 执行部分(Executable Part)
   Ada.Text_IO.Put_Line ("Hello, Ada World!");
end Hello;

Ada程序的四大组成部分

部分关键字作用可否为空
上下文子句with / use引入外部依赖可以
单元头procedure / function / package程序单元类型和名称不可
声明部分isbegin 之间局部常量、变量、类型、子程序可以
执行部分beginend 之间实现具体逻辑不可

1.2 最简单的合法程序

procedure Minimal is
begin
   null;  -- 空语句,表示"什么都不做"
end Minimal;

null 语句是Ada的占位符,用于语法上必须有语句但逻辑上无需操作的场景。


二、上下文子句深度解析

2.1 with 子句的本质

with 不是简单的"文件包含",而是语义化的单元依赖声明

with Ada.Text_IO;           -- 引入标准输入输出包
with Ada.Integer_Text_IO;   -- 引入整数输入输出包
with Ada.Float_Text_IO;     -- 引入浮点数输入输出包

与C语言 #include 的核心区别:

特性C #includeAda with
机制文本替换语义检查
重复引用需用 #ifndef 防护自动处理,无重复问题
依赖分析预处理器处理编译器原生支持,可精准追踪
信息隐藏头文件暴露所有细节规范(.ads)控制可见性

2.2 use 子句:简化访问

频繁使用 Ada.Text_IO.Put_Line 很冗长,可用 use

with Ada.Text_IO;
use Ada.Text_IO;  -- 将包的内容直接引入当前作用域

procedure Greetings is
begin
   Put_Line ("Hello!");      -- 无需前缀
   Put_Line ("Goodbye!");    -- 直接调用
end Greetings;

对比两种风格

风格代码适用场景
全限定Ada.Text_IO.Put_Line (...)大型项目,避免命名冲突
use字句Put_Line (...)小型程序,提高可读性

工业建议:规范文件(.ads)中避免 use ,包体(.adb)中可酌情使用。


2.3 use type :精确控制

只想引入某个类型的操作符,而非整个包:

with Ada.Calendar;
use type Ada.Calendar.Time;  -- 只引入Time类型的操作符

procedure Time_Example is
   T1, T2 : Ada.Calendar.Time;
begin
   if T1 < T2 then  -- 可直接使用<操作符
      ...
   end if;
end Time_Example;

三、标识符与保留字

3.1 标识符命名规则

-- 合法标识符
Hello_World
Get_Value
Temperature_2
HTTP_Request   -- 连续下划线非法!应为HTTP_Request或Http_Request

-- 非法标识符
2nd_Value      -- 数字开头
Get-Value      -- 连字符非法(这是减号)
Hello__World   -- 连续下划线非法
hello world    -- 空格非法

Ada标识符规则

  • 必须以字母开头

  • 只能包含字母、数字、下划线

  • 不能连续使用两个下划线

  • 不能以数字开头

  • 不区分大小写HelloHELLO 相同)


3.2 保留字(关键字)

Ada有73个保留字,不可用作标识符:

类别保留字
声明procedure , function , package , type , subtype , constant
控制if , then , else , elsif , case , when , loop , while , for , exit , return
并发task , entry , accept , select , delay , abort
异常exception , raise , begin , end
其他is , in , out , access , new , null , others

完整列表见Ada Reference Manual


3.3 命名规范(工业标准)

元素规范示例
包/过程/函数PascalCaseAda.Text_IO , Get_Value
变量/常量Snake_Casecurrent_temperature , Max_Size
常量大写PascalCasePI , Buffer_Size
类型/子类型PascalCaseInteger , Positive , My_Record

四、注释与文档

4.1 单行注释

-- 这是单行注释,从--到行尾
X := 10;  -- 行尾注释,解释X的初始值

4.2 多行注释(Ada无原生支持)

Ada标准没有 /* */ 风格的多行注释,需每行加 --

-- 这是第一段说明
-- 这是第二段说明
-- 这是第三段说明
X := 20;

历史原因:Ada设计于1980年,当时认为多行注释容易导致注释嵌套混乱,且不利于工具处理。

4.3 文档注释(AdaDoc风格)

-- 计算圆的面积
-- 参数:
--   Radius - 圆的半径,必须为正数
-- 返回:
--   圆的面积
function Circle_Area (Radius : Float) return Float is
begin
   return 3.14159 * Radius ** 2;
end Circle_Area;

五、声明部分详解

5.1 可以声明什么?

procedure Declarations is
   
   -- 1. 常量声明
   Pi : constant Float := 3.14159;
   
   -- 2. 变量声明
   Counter : Integer := 0;
   
   -- 3. 类型声明
   type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
   
   -- 4. 子类型声明
   subtype Weekday is Day range Mon .. Fri;
   
   -- 5. 子程序声明(前向声明)
   procedure Helper (X : in out Integer);
   
   -- 6. 其他包的使用
   package My_IO renames Ada.Text_IO;
   
begin
   -- 执行部分可以使用上述所有声明
   Counter := Counter + 1;
   Helper (Counter);
end Declarations;

-- 子程序体必须在主单元之后或单独文件中
procedure Helper (X : in out Integer) is
begin
   X := X * 2;
end Helper;

__

5.2 声明的顺序规则

重要:Ada要求先声明后使用

procedure Order_Matters is
   A : Integer := B;  -- ERROR! B尚未声明
   B : Integer := 10;
begin
   null;
end Order_Matters;

正确写法

procedure Correct_Order is
   B : Integer := 10;  -- 先声明B
   A : Integer := B;   -- 再使用B,合法
begin
   null;
end Correct_Order;

六、执行部分详解

6.1 语句序列

beginend 之间是语句序列,每条语句以分号结束:

procedure Statements is
begin
   -- 过程调用语句
   Ada.Text_IO.Put_Line ("First");
   
   -- 赋值语句
   X := 10;
   
   -- 控制语句
   if X > 5 then
      Ada.Text_IO.Put_Line ("Big");
   end if;
   
   -- 空语句(合法但无操作)
   null;
end Statements;

6.2 语句的终止

元素是否需要分号示例
语句必须X := 10;
声明必须A : Integer;
end必须end Hello;
begin不需要begin
loop , if , then不需要if X > 5 then

七、完整示例:温度转换器

-- 文件名: temperature_converter.adb
-- 功能:摄氏度与华氏度相互转换

with Ada.Text_IO;
with Ada.Float_Text_IO;

procedure Temperature_Converter is
   
   -- 常量声明
   Freezing_C : constant Float := 0.0;
   Freezing_F : constant Float := 32.0;
   Factor     : constant Float := 5.0 / 9.0;
   
   -- 变量声明
   Celsius    : Float;
   Fahrenheit : Float;
   
begin
   
   -- 摄氏度转华氏度
   Celsius := 100.0;
   Fahrenheit := Freezing_F + Celsius / Factor;
   
   Ada.Text_IO.Put ("Celsius: ");
   Ada.Float_Text_IO.Put (Celsius, Fore => 3, Aft => 1, Exp => 0);
   Ada.Text_IO.Put (" => Fahrenheit: ");
   Ada.Float_Text_IO.Put (Fahrenheit, Fore => 3, Aft => 1, Exp => 0);
   Ada.Text_IO.New_Line;
   
   -- 华氏度转摄氏度
   Fahrenheit := 212.0;
   Celsius := Factor * (Fahrenheit - Freezing_F);
   
   Ada.Text_IO.Put ("Fahrenheit: ");
   Ada.Float_Text_IO.Put (Fahrenheit, Fore => 3, Aft => 1, Exp => 0);
   Ada.Text_IO.Put (" => Celsius: ");
   Ada.Float_Text_IO.Put (Celsius, Fore => 3, Aft => 1, Exp => 0);
   Ada.Text_IO.New_Line;
   
end Temperature_Converter;

编译运行

gnatmake temperature_converter.adb
./temperature_converter

输出

Celsius: 100.0 => Fahrenheit: 212.0
Fahrenheit: 212.0 => Celsius: 100.0

八、常见编译错误速查

错误信息原因解决
missing ";"语句末尾缺少分号添加 ;
incorrect use of "with"with 子句位置错误移到最开头
identifier cannot be use clause nameuse 后直接跟包名,缺少 ;with P; use P;
declaration expected声明部分写了执行语句移到 begin
statement expectedbegin 后写了声明移到 isbegin 之间

九、本课总结

  • Ada程序由上下文子句、单元头、声明部分、执行部分四部分组成

  • with 引入依赖, use 简化访问,规范中避免 use

  • 标识符不区分大小写,不能连续下划线,不能以数字开头

  • 注释只有 -- 单行形式,文档化注释需每行加 --

  • 先声明后使用是铁律,声明在 isbegin 之间,执行语句在 beginend 之间


十、课后练习

1.修正错误:以下代码有3处错误,找出并修正:

procedure Broken is
begin
   with Ada.Text_IO;
   Ada.Text_IO.Put_Line ("Test")
end broken;

2.规范命名:将以下不规范标识符改为Ada标准形式:

getValue → ?

2nd_item → ?

HTTP__Request → ?

3.扩展程序:修改温度转换器,增加开尔文温度转换(K = C + 273.15)。

4.代码风格:重写Hello World,使用 use Ada.Text_IO; ,对比可读性差异。


十一、下节预告

第4课|程序结构与编译流程

我们将:

  • 理解库单元、子单元、主程序的层级关系

  • 掌握 gnatmake 的依赖分析原理

  • 学习分离编译与分别编译的区别

  • 构建第一个多文件Ada项目


关键术语表

  • 上下文子句withuse 子句的统称,用于管理单元依赖

  • 声明部分isbegin 之间的区域,用于定义局部实体

  • 执行部分beginend 之间的区域,包含实际执行的语句

  • 保留字:Ada语言预定义的关键字,不可用作标识符

  • 前向声明:先声明子程序签名,稍后提供实现的机制


提示警告:本课程内容(包括但不限于文字、图片、音频、视频等)版权归原作者所有,未经授权严禁转载、复制、翻录、传播或以任何方式用于商业用途。本课程仅供个人学习使用,请尊重知识产权,共同维护良好的创作环境。如有疑问或需授权合作,请联系版权方。感谢您的理解与支持!