单元(Units)

单元(Unit)可以把共用的类型、函数、过程、常量、变量等声明打包,供其他单元或主程序调用,等价于其他编程语言里的模块 / 包

创建单元

单元分为两大核心区块:

interface 接口区:对外暴露的内容,其他文件引入后可直接访问;

implementation 实现区:内部私有代码,仅当前单元可见,外部无法调用。

例子

创建一个叫MyUnit.pas的文件,用来写其中一个单元。

unit MyUnit;

{$ifdef FPC}
{$mode objfpc}{$H+}{$J-}
{$endif}

// 接口区:对外公开
interface

procedure MyProcedure(const A: Integer);
function MyFunction(const S: string): string;

// 实现区:私有实现代码
implementation

procedure MyProcedure(const A: Integer);
begin
  WriteLn('A + 10 is: ', A + 10);
end;

function MyFunction(const S: string): string;
begin
  Result := S + 'strings are automatically managed';
end;

end.Code language: JavaScript (javascript)

细心的读者会发现,末尾的end. 没有对应的begin?

Pascal 的单元(unit)语法结构和程序(program)不同

program 主程序:必须有 begin ... end. 作为程序执行入口

unit 单元:不需要全局 begin

整个单元结尾:end.(点号结尾,代表文件结束,无对应全局 begin)

  • interface 里写函数 / 过程原型、类型、常量、全局变量:外部 uses 后就能调用;
  • implementation 只写函数完整实现,里面定义的局部过程、私有变量,外部单元不可见;
  • 单元文件以 unit 名称; 开头,end. 结尾。

主程序引入单元

我们的主程序文件是mydemo.pas

通过 uses 关键字加载自定义单元

{$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif}
{$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif}

program MyProgram;

// 引入自定义单元 MyUnit
uses
  MyUnit;

begin
  WriteLn(MyFunction('Note: '));
  MyProcedure(5);
  ReadLn;
end.Code language: PHP (php)

上面的uses 下面的MyUnit; 是单元名称,不是文件pas的名称。

也就是 是MyUnit.pas里面的第一句代码,unit MyUnit; 的这个名称。

尽管可以文件名和单元名不一样,但是建议还是让单元名和文件名同名。如果不同名,这里的uses 也是使用单元名,而不是文件名。

现在我们编译运行。主程序中调用了单元中的函数。

现在我们编译运行

编译后,读者会发现,项目目录下面,多了两个文件

.ppu(Pascal Unit File,单元编译信息文件)

仅单元文件(unit)编译才会生成,主程序 program 没有 ppu

  • 存储单元 interface 接口区的编译元数据:函数原型、类型、常量、导出符号;
  • 后续其他文件 uses MyUnit 时,编译器不需要重新解析 MyUnit.pas 完整源码,直接读取 .ppu 快速校验接口;
  • 大幅提升大型项目编译速度,是 FPC 增量编译的核心文件。

MyUnit.o:MyUnit.pas 编译后的二进制机器指令(实现区完整代码)

mydemo.o:mydemo.pas 主程序的机器码

编译器先把 pas 翻译成独立二进制 .o,最后链接器把所有 .o 合并,打包成可执行程序 .exe

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注