第5课|标识符与保留字

30 阅读11分钟

本课目标

完成本课后,你将能够:

  • 完整掌握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_非法
不区分大小写HelloHELLOhello相同编译器视为同一标识符
无长度限制但建议保持可读性最长支持实现定义

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_IOmathutils, textio
过程/函数PascalCase,动词开头Get_Value, Calculate_AreagetValue, calc
类型名PascalCase,名词Customer_ID, Buffer_SizecustomerId, buffer_size
变量名Snake_Casecurrent_index, total_countCurrentIndex, totalCount
常量大写Snake_CaseMAX_SIZE, BUFFER_CAPACITYMaxSize, max_size
枚举值PascalCaseRed, Green, BlueRED, red
泛型形参大写单字母或PascalCaseT, Element_Typeelement, 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使用保留字作标识符改名,如TypeData_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.保留字分类:将 taskreturnprivatedeltaxor 按类别归类。

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:单词间以下划线分隔的命名风格


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