本课目标
完成本课后,你将能够:
-
完整掌握Ada标识符的命名规则与限制
-
熟练识别并正确使用73个Ada保留字
-
理解属性标识符与转义标识符的特殊用法
-
应用工业级编码规范提升代码可读性
-
使用
gnatcheck工具进行代码风格检查
一、标识符基础规则
1.1 合法标识符的构成
Ada标识符由字母、数字和下划线组成,遵循严格规则:
-- 合法标识符
X
Count
Get_Value
Sensor_1
HTTP_Request
Αρχή -- 希腊字母(Unicode支持)
变量 -- 中文字符(GNAT支持,但不推荐)
-- 非法标识符
1st_Value -- 错误:数字开头
Get-Value -- 错误:含连字符(这是减号运算符)
Get__Value -- 错误:连续两个下划线
Get_ -- 错误:以下划线结尾
_ -- 错误:单独下划线
核心规则总结:
| 规则 | 说明 | 示例 |
|---|---|---|
| 首字符必须是字母 | 数字和下划线不能开头 | A1合法,1A非法 |
| 不能连续下划线 | 单下划线可用,双下划线禁用 | A_B合法,A__B非法 |
| 不能以下划线结尾 | 末尾必须是字母或数字 | A_B合法,A_非法 |
| 不区分大小写 | Hello、HELLO、hello相同 | 编译器视为同一标识符 |
| 无长度限制 | 但建议保持可读性 | 最长支持实现定义 |
1.2 大小写不敏感的深层含义
-- 以下声明在Ada中是同一标识符,会导致重复定义错误
procedure Example is
Count : Integer := 0;
COUNT : Integer := 1; -- ERROR! 与Count冲突
count : Integer := 2; -- ERROR! 同样冲突
begin
null;
end Example;
工业建议:虽然Ada不区分大小写,但保持一致的命名风格至关重要。
二、保留字全景解析
2.1 保留字总览
Ada 2012共有73个保留字,分为9个类别:
1. 程序单元声明(10个)
| 保留字 | 用途 | 示例 |
|---|---|---|
procedure | 声明过程 | procedure Hello is |
function | 声明函数 | function Add (A, B : Integer) return Integer |
package | 声明包 | package Math is |
task | 声明任务 | task type Server is |
protected | 声明保护对象 | protected type Buffer is |
generic | 声明泛型单元 | generic type T is private; |
body | 包体/任务体等 | package body Math is |
is | 单元头分隔符 | procedure P is |
separate | 子单元声明 | separate (Parent) |
renames | 重命名 | package IO renames Ada.Text_IO; |
2. 类型与对象声明(9个)
| 保留字 | 用途 | 示例 |
|---|---|---|
type | 类型声明 | type Day is (Mon, Tue, Wed); |
subtype | 子类型声明 | subtype Positive is Integer range 1 .. Integer'Last; |
constant | 常量声明 | Pi : constant := 3.14159; |
access | 访问类型(指针) | type Int_Ptr is access Integer; |
array | 数组类型 | type Vector is array (1 .. 10) of Float; |
record | 记录类型 | type Point is record X, Y : Float; end record; |
range | 范围约束 | subtype Small is Integer range -10 .. 10; |
of | 数组元素类型 | array (1 .. 10) of Integer |
digits | 浮点精度 | type My_Float is digits 6; |
3. 控制结构(12个)
| 保留字 | 用途 | 示例 |
|---|---|---|
if / then / else / elsif | 条件分支 | if X > 0 then ... elsif X < 0 then ... else ... |
case / when / others | 多路分支 | case Day is when Mon => ... when others => ... |
loop / while / for | 循环 | while Condition loop ..., for I in 1 .. 10 loop |
exit | 退出循环 | exit when Found; |
goto | 无条件跳转(极少用) | goto Label; |
4. 异常处理(3个)
| 保留字 | 用途 | 示例 |
|---|---|---|
exception | 异常声明/处理器 | when Constraint_Error => ... |
raise | 抛出异常 | raise Program_Error; |
handler | (非保留,但常用) | 异常处理器语法 |
5. 并发与实时(8个)
| 保留字 | 用途 | 示例 |
|---|---|---|
entry | 任务入口 | entry Get (X : out Item); |
accept | 接受入口调用 | accept Get (X : out Item) do ... |
select | 选择性等待 | select when Guard => accept ... |
delay | 延迟执行 | delay 1.0; -- 延迟1秒 |
abort | 终止任务 | abort T; |
terminate | 任务终止替代 | select terminate; end select; |
requeue | 重新排队入口 | requeue Other_Entry; |
synchronized | (Ada 2022)同步接口 | 本课程不涉及 |
6. 面向对象(5个)
| 保留字 | 用途 | 示例 |
|---|---|---|
tagged | 标记类型(OOP基础) | type Shape is tagged record ... |
abstract | 抽象类型/子程序 | type Shape is abstract tagged ... |
interface | 接口类型 | type Drawable is interface; |
overriding | 显式覆盖标记 | overriding procedure Draw (S : Circle); |
limited | 有限类型(禁止赋值) | type File is limited private; |
7. 泛型与参数(6个)
| 保留字 | 用途 | 示例 |
|---|---|---|
private | 私有类型/部分 | private type Stack is ... |
with | 引入依赖/泛型参数 | with Ada.Text_IO; / generic with function "+" ... |
new | 泛型实例化/派生 | package Int_Stack is new Stack (Integer); |
mod | 取模运算/泛型约束 | X mod Y / generic type T is mod <>; |
delta | 定点精度 | type Fixed is delta 0.01 range -1.0 .. 1.0; |
at | 表示子句(地址指定) | for X'Address use ... |
8. 其他关键字(15个)
| 保留字 | 用途 | 说明 |
|---|---|---|
begin / end | 执行部分界定 | begin ... end Procedure_Name; |
declare | 块声明 | declare X : Integer; begin ... end; |
return | 函数返回 | return Result; |
null | 空语句/空值 | null; / access procedure is null; |
in | 参数模式/集合成员 | A in 1 .. 10 / for X in Set |
out | 输出参数模式 | procedure P (X : out Integer); |
not | 逻辑/成员取反 | not Flag, not in |
and / or / xor | 逻辑运算 | 支持短路形式 and then, or else |
rem | 取余运算 | A rem B(与mod符号处理不同) |
abs | 绝对值 | abs X |
in(参数模式) | 输入参数(默认) | procedure P (X : in Integer) |
9. 特殊用途(5个)
| 保留字 | 用途 | 说明 |
|---|---|---|
all | 访问类型解引用 | Ptr.all := 10; |
some | 存在量词(谓词) | if X = (for some Y in 1 .. 10 => Y * Y = X) |
synchronized | 同步接口(Ada 2022) | 本课程不涉及 |
parallel | 并行块(Ada 2022) | 本课程不涉及 |
until | 条件表达式(Ada 2022) | 本课程不涉及 |
2.2 保留字使用禁忌
-- 错误:将保留字用作标识符
procedure Procedure is -- ERROR! procedure是保留字
Type : Integer; -- ERROR! Type是保留字
begin
null;
end Procedure;
编译错误示例:
error: reserved word "procedure" cannot be used as identifier
三、属性标识符(Attributes)
3.1 什么是属性?
属性是以撇号(')为前缀的预定义标识符,用于查询类型或对象的特性:
-- 类型属性
Integer'First -- Integer类型的最小值(-2^31)
Integer'Last -- Integer类型的最大值(2^31-1)
Float'Digits -- Float类型的十进制精度位数
-- 数组属性
Array'Length -- 数组长度
Array'Range -- 数组索引范围
Array'First -- 数组第一个索引
Array'Last -- 数组最后一个索引
-- 标量属性
X'Image -- 值的字符串表示
X'Value ("123") -- 将字符串转为值
X'Size -- 对象占用的位数
X'Address -- 对象的内存地址
3.2 常用属性速查表
| 属性 | 适用对象 | 返回值 | 示例 |
|---|---|---|---|
'First | 标量类型/数组 | 第一个值/索引 | Integer'First = -2147483648 |
'Last | 标量类型/数组 | 最后一个值/索引 | Integer'Last = 2147483647 |
'Range | 数组 | 索引范围 | A'Range = A'First .. A'Last |
'Length | 数组 | 元素个数 | A'Length |
'Size | 类型/对象 | 存储位数 | Integer'Size = 32 |
'Image | 标量对象 | 字符串 | 123'Image = " 123" |
'Value | 标量类型 | 解析字符串 | Integer'Value ("42") = 42 |
'Pos | 枚举类型 | 位置编号 | Day'Pos (Mon) = 0 |
'Val | 枚举类型 | 编号转值 | Day'Val (0) = Mon |
'Succ | 离散类型 | 后继值 | Day'Succ (Mon) = Tue |
'Pred | 离散类型 | 前驱值 | Day'Pred (Tue) = Mon |
'Class | 标记类型 | 类广类型 | Shape'Class |
3.3 属性使用示例
with Ada.Text_IO;
procedure Attribute_Demo is
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
subtype Weekday is Day range Mon .. Fri;
Arr : array (1 .. 10) of Integer;
begin
-- 枚举属性
Ada.Text_IO.Put_Line ("First day: " & Day'Image (Day'First));
Ada.Text_IO.Put_Line ("Weekday range: " &
Day'Image (Weekday'First) & " to " &
Day'Image (Weekday'Last));
-- 数组属性
Ada.Text_IO.Put_Line ("Array length: " & Integer'Image (Arr'Length));
Ada.Text_IO.Put_Line ("Array indices: " &
Integer'Image (Arr'First) & " .. " &
Integer'Image (Arr'Last));
-- 数值属性
Ada.Text_IO.Put_Line ("Integer min: " & Integer'Image (Integer'First));
Ada.Text_IO.Put_Line ("Integer max: " & Integer'Image (Integer'Last));
end Attribute_Demo;
四、转义标识符(Escaped Identifiers)
当需要:
-
使用保留字作为标识符(不推荐但可能)
-
包含特殊字符或空格
-
区分大小写(与外部系统交互)
可使用转义标识符,以 Q 开头,用 # 或 " 界定:
-- 使用保留字作为标识符(不推荐,但合法)
procedure Q#procedure# is -- 转义后procedure可作为标识符
begin
null;
end Q#procedure#;
-- 包含特殊字符
X : Integer := Q#"Hello World"#; -- 标识符含空格!
-- 区分大小写(GNAT特定扩展)
Q#"Hello"# 和 Q#"HELLO"# -- 被视为不同标识符
警告:转义标识符是实现定义特性,GNAT支持但可能不移植。生产代码应避免。
五、工业级编码规范
5.1 命名约定(GNAT编码标准)
| 元素 | 规范 | 示例 | 反例 |
|---|---|---|---|
| 包名 | PascalCase,复数名词 | Math_Utils, Text_IO | mathutils, textio |
| 过程/函数 | PascalCase,动词开头 | Get_Value, Calculate_Area | getValue, calc |
| 类型名 | PascalCase,名词 | Customer_ID, Buffer_Size | customerId, buffer_size |
| 变量名 | Snake_Case | current_index, total_count | CurrentIndex, totalCount |
| 常量 | 大写Snake_Case | MAX_SIZE, BUFFER_CAPACITY | MaxSize, max_size |
| 枚举值 | PascalCase | Red, Green, Blue | RED, red |
| 泛型形参 | 大写单字母或PascalCase | T, Element_Type | element, t |
5.2 代码风格检查:gnatcheck
GNAT提供 gnatcheck 工具,自动检查代码规范:
创建规则文件 coding_standard.txt :
-- 启用GNAT编码风格检查
+R Identifiers_Casing
+R Identifier_Suffixes
+R Identifier_Prefixes
执行检查:
gnatcheck -rules coding_standard.txt *.adb *.ads
常用检查规则:
| 规则 | 说明 |
|---|---|
Identifiers_Casing | 检查标识符大小写规范 |
Identifier_Suffixes | 检查类型名后缀(如_Type) |
Identifier_Prefixes | 检查特定前缀使用 |
Too_Many_Primitives | 限制类型原始操作数量 |
Deep_Inheritance | 限制继承深度 |
六、完整示例:规范命名实践
-- 文件名: employee_manager.ads
-- 规范:员工管理包
package Employee_Manager is
-- 类型声明
type Employee_ID is range 1 .. 10_000;
type Department is (HR, Engineering, Sales, Support);
-- 常量
Max_Employees : constant := 1_000;
Default_Department : constant Department := Engineering;
-- 子程序
procedure Hire_Employee
(Name : in String;
Dept : in Department := Default_Department;
New_ID : out Employee_ID);
function Get_Department (ID : Employee_ID) return Department;
function Is_Valid_ID (ID : Employee_ID) return Boolean;
private
-- 私有实现细节
type Employee_Record is record
ID : Employee_ID;
Name : String (1 .. 50);
Length : Natural;
Belongs_To : Department;
end record;
end Employee_Manager;
-- 文件名: employee_manager.adb
with Ada.Strings.Fixed;
package body Employee_Manager is
Employee_DB : array (Employee_ID) of Employee_Record;
Next_ID : Employee_ID := Employee_ID'First;
procedure Hire_Employee
(Name : in String;
Dept : in Department := Default_Department;
New_ID : out Employee_ID)
is
begin
if Next_ID > Employee_ID'Last then
raise Constraint_Error with "Employee database full";
end if;
New_ID := Next_ID;
Employee_DB (New_ID).ID := New_ID;
Employee_DB (New_ID).Belongs_To := Dept;
-- 安全复制字符串
declare
Len : constant Natural := Natural'Min (Name'Length, 50);
begin
Employee_DB (New_ID).Name (1 .. Len) := Name (Name'First .. Name'First + Len - 1);
Employee_DB (New_ID).Length := Len;
end;
Next_ID := Next_ID + 1;
end Hire_Employee;
function Get_Department (ID : Employee_ID) return Department is
begin
return Employee_DB (ID).Belongs_To;
end Get_Department;
function Is_Valid_ID (ID : Employee_ID) return Boolean is
begin
return ID >= Employee_ID'First and ID < Next_ID;
end Is_Valid_ID;
end Employee_Manager;
七、常见错误与纠正
| 错误代码 | 问题 | 纠正 |
|---|---|---|
illegal identifier | 数字开头或含非法字符 | 改为字母开头,移除特殊字符 |
reserved word | 使用保留字作标识符 | 改名,如Type→Data_Type |
consecutive underlines | 连续下划线 | 删除多余下划线 |
identifier too long | 标识符过长(警告) | 缩短至有意义的最短长度 |
casing does not match | 大小写不一致(GNAT警告) | 统一使用声明时的大小写 |
八、本课总结
-
Ada标识符不区分大小写,必须以字母开头,禁止连续下划线
-
73个保留字涵盖程序单元、类型、控制、并发、OOP等全部特性
-
属性标识符('First、'Last等)是查询类型特性的强大工具
-
转义标识符(Q#...#)可突破限制,但生产代码应避免
-
工业规范要求一致的命名风格,
gnatcheck可自动验证
九、课后练习
1.识别错误:以下标识符哪些非法?说明原因:
-
2nd_Place -
Get__Value -
End_ -
type -
HTTP_Request
2.保留字分类:将 task 、 return 、 private 、 delta 、 xor 按类别归类。
3.属性实践:编写程序输出 Float 类型的所有标准属性值('First、'Last、'Digits等)。
4.规范重构:将以下不规范代码按GNAT标准重写:
procedure getData (x : in out integer) is
MAXVAL : constant := 100;
begin
x := x + 1;
end getData;
5.gnatcheck实践:对前几课代码运行 gnatcheck ,修复所有风格警告。
十、下节预告
第6课|注释与代码风格
我们将:
-
掌握Ada注释的完整语法与文档化规范
-
学习使用AdaDoc工具生成API文档
-
理解代码布局、缩进、空行的最佳实践
-
配置编辑器自动格式化与风格检查
关键术语表
保留字:Ada语言预定义的具有特殊语法含义的标识符
属性:以撇号前缀查询类型或对象特性的标识符
转义标识符:以Q开头的特殊标识符形式,可包含保留字或特殊字符
PascalCase:每个单词首字母大写的命名风格
Snake_Case:单词间以下划线分隔的命名风格
提示警告:本课程内容(包括但不限于文字、图片、音频、视频等)版权归原作者所有,未经授权严禁转载、复制、翻录、传播或以任何方式用于商业用途。本课程仅供个人学习使用,请尊重知识产权,共同维护良好的创作环境。如有疑问或需授权合作,请联系版权方。感谢您的理解与支持!