Object Pascal 语法之语言基础(四)

1.8 过程与函数

过程与函数是实现一定功能的语句块,是程序中的特定功能单元。可以在程序的其他地方被调用,也可以进行递归调用。过程与函数的区别在于过程没有返回值,而函数有返回值。

1.过程与函数的定义
过程与函数的定义包括过程原型或函数原型、过程体或函数体的定义。过程定义的形式如下:

procedure ProcedureName(ParameterList); directives;
var
    LocalDeclarations;
begin
    statements
end;

ProcedureName 是过程名,是有效的标识符。ParameterList 为过程的参数列表,需要指明参数的个数和数据类型。Directives 是一些关于函数的指令字, 如果设置多个, 应该用分号隔开。LocalDeclarations 中定义了该函数中需要使用的一些临时变量,通常也称作本地变量。在Begin 与End 之间是过程调用时实现特定功能的一系列语句。ParameterList、Directives、LocalDeclarations 和Statements 都是可选部分。

函数的定义与过程非常类似,只是使用的保留字不同,而且多了一个返回值类型。具体形式如下:

function FunctionName(ParameterList): ReturnType; directives;
var
    LocalDeclarations;
begin
    statements
end;

可以将函数需要返回的数值赋值给变量Result。如果函数体中存在着一些由于判断而产生的分支语句时,就要在每一个分支中设置返回值。通常要根据函数的返回值来确定下一步的操作。注意,这

里与Visual C 和Visual C++不一样,把一个值赋给Result,函数并不会结束。

2.参数
函数定义时参数列表中的参数称为形参,函数调用时参数列表中的参数称为实参。在定义的函数原型中,多个参数之间用分号隔开,同一类型的参数可以放在一起,以逗号隔开。函数调用的时候,在函数原型中,多个参数之间用逗号隔开。
一般来说,形参列表和实参列表完全匹配是指参数的个数一样,而且顺序排列的数据类型也完全一致。对于普通的函数,如果编译器发现实参的数据类型与形参的数据类型不匹配,会将实参的数据类型进行一次或多次的“提升”,比如把Integer 类型转换为Double 类型。可以为过程和函数的参数指定默认数值。指定默认数值的参数要放在参数列表的后部,而没有指定默认数值的参数要放在参数列表的前
部。在函数调用的时候,可以为设置了默认值的参数指定一个新值。在函数体中,各语句使用的是指定的新值,如果没有指定新值,则使用默认值。同样,如果存在多个设置了默认值的参数,只有在前
面的参数指定了新值后,后面的参数才可以指定新值。
下面的例子定义了一个OutputNum 函数,可以将一个浮点数按指定的精度输出。通过这个例子,读者可以体会函数中参数的使用:

program Project1;
{$APPTYPE CONSOLE}

uses Sysutils; //为了使用函数Format

function OutputNum(number:double;n:integer = 5):Boolean;
var
    Str : string; //浮点数显示输出的内容

begin
    if n <= -1 then //小数点后的位数要大于或等于0
        begin
            Result:=False;
            Exit; //退出显示函数
        end
    else
        begin
            // 设置显示的格式
            Str := Format(‘%*.*f‘, [10, n, number]);
            Result := True ;
            Writeln(Str); //显示数据
        end;
end;

begin
    OutputNum(12.345); //n 默认为5
    OutputNum(123,3); //参数对数据类型进行升级
    //下面一句代码不正确,故屏蔽掉
    //OutputNum(123.456789,9.13); //参数对数据类型不能降级
    //可以根据函数的返回值确定下一步的操作
    if OutputNum(123.456789,-3) = False then
        Writeln(‘输出失败。‘) ;
    Readln;
end.

运行结果如下:

12.34500
123.000
输出失败。

这里有几点需要说明:

  • 为了使用函数Format,需要在Uses 语句中将Sysutils 单元包含进去。
  • 由于小数点后的位数不可以设置为负数,所以当出现负数时,OutputNum 函数返回False,并调用Exit 函数立刻退出OutputNum 函数。
  • 在语句OutputNum(123,3)中,首先将整型常数123 转换为浮点型常数,然后进行参数传递。

最常用的参数有3 种,分别为数值参数变量参数常量参数
数值参数在运行过程中只改变其形参的值,不改变其实参的值,即参数的值不能传递到过程的外面。试看下面的例程:

procedure Calculate(CalNo:Integer);
begin
    CalNo := CalNo*10;
end;

用以下例程调用Calculate 函数:

Calculate(Number);

Number 进入Calculate 函数后,会把Number 实参拷贝给形参CalNo,在此过程中CalNo 增大10倍,但并未传递出来,所以说Number 值并未改变。形参和实参占用不同的内存地址,在过程或函数被调用时,将实参的值复制到形参占用的内存中。因此,在跳出过程或函数后,形参和实参的数值是不同的,但实参的值并不发生变化。

如果想改变传入的参数值,就需要使用变量参数,即在被调用程序的参数表中的形参前加上保留字Var。例如:

procedure Calculate(var CalNo : Integer);

则CalNo 并不在内存中占据一个位置,而是指向实参Number。当一个实参被传递时,任何对形参所作的改变都会反映到实参中,这是因为两个参数指向同一个地址。将上一个例程中的形参CalNo前面加上Var,再以同样的程序调用它,则在第2 个编辑框中会显示计算的结果,把第1 个编辑框中的数值放大10 倍。这时形参CalNo 和实参Number 的值都是Nnmber 初始值的10 倍。

如果过程或函数执行时要求不改变形参的值,最有保证的办法是使用常量参数。在参数表的参数名称前加上保留字Const 就可以使一个形参成为常量参数。使用常量参数代替数值参数可以保护用户的参数,使用户在不想改变参数值时不会意外地将新的值赋给这个参数。下面的例子可以帮助读者加深理解:

program Project1;
{$APPTYPE CONSOLE}

type
    PInteger = ^Integer; //定义指针类型

procedure P1(var N:Integer); //引用参数传递
begin
    N:=N+1 ;
end;

procedure P2(N:Integer); //普通参数传递
begin
    N:=N+2;
end;

procedure P3(PT:PInteger); //传递指针参数
begin
    PT^:=PT^+3;
end;

var
    i:Integer;
begin
    i:=1;
    P1(i); //将i 的值增加1
    Writeln(‘i:‘,i);
    P2(i); //希望将i 加2,但没有实现
    Writeln(‘i:‘,i);
    P3(@i); //将i 加3
    Writeln(‘i:‘,i);
    Readln;
end.

运行结果如下:

i:2
i:2
i:5

这里有几点需要说明:

  • 一开始变量i 的数值为1,经过P1 过程的处理,将i 加1,所以显示的第1 个i 的数值为2,这时使用的是引用参数传递。
  • 在过程P2 中,将形参的数值增加了2,实际上i 并没有增加,所以显示的第2 个i 的数值仍然为2。在这种情况下,正常的做法可以使用函数的返回值,例如:
Result:=N+2;

在调用函数的时候使用:

i:=P2(i);
  • 在过程P3 中,传递的是变量I 的指针,所以操作是针对i 进行的,第3 次显示i 的数值是5。

3.过程与函数的调用约定
  在调用过程或函数的时候,如果参数列表中具有多个参数,那么参数传递给过程或函数的顺序会对结果产生一定的影响。对于不同的语言,参数传递的顺序是不同的:Pascal 语言是按照从左向右的顺序进行传递的,而C 语言是按照从右向左的顺序来传递的。

  为了确定传递的顺序,可以在过程或函数定义的时候,在Directives 部分利用指令字指定传递的顺序。来自Delphi 的联机帮助的数据,如表1-13 所示,其中列举了Directives 部分可使用的关于函数调用约定的指令字。
表1-13 定义过程与函数时对调用约定起作用的指令字

Directive 指令 Register Pascal Stdcall safecall Cdecl
参数传递顺序 从左向右 从左向右 从右向左 从右向左 从右向左

可以通过下面的例子查看参数传递的顺序:

program Project1;
{$APPTYPE CONSOLE}

function P1:Integer; //该函数将作为GetMax 函数的第1 个参数
begin
    Writeln(‘P1‘);
    Result:=0;
end;

function P2:Integer; //该函数将作为GetMax 函数的第2 个参数
begin
    Writeln(‘P2 ‘) ;
    Result:=1;
end;

//参数的传递方式采用pascal 方式
function GetMax(N1:Integer; N2:Integer):Integer;pascal;
begin
    Result:=N1+N2;
end;

begin
    GetMax(P1,P2);
end.

运行结果如下:


1

2

P1

P2

如果将GetMax 函数定义处的Directives 部分由Pascal 改为Stdcall,则运行结果变为:

P2
P1

用户可以修改GetMax 函数定义处的Directives 部分为表1-13 中的其他数值,测试结果是否一致。

4.过程和函数的重载
可以在同一个作用范围内给不同的过程或函数取同一个名称,这种现象就叫做重载。这样可以使编程更方便。在重载的情况下,决定使用哪个过程或函数的依据是形参和实参的一致性,即参数个数、参数类型以及它们之间的顺序,不存在一个函数调用满足两个重载函数的情况。另外重载函数必须用指令字Overload 来进行说明,函数的返回值类型不同就不可以作为重载函数的依据。下面的两个函数
就是重载函数:

function Average(a:Integer; b:Integer):Double;overload; //求整形数据的平均值
function Average(a:Double; b:Double):Double;overload; //求实数数据的平均值

下面两条语句就调用了不同的函数:

Average(3.7,4.6); //调用的是第2 个重载函数
Average(3,4); //调用的是第1 个重载函数

如果又定义了一个重载函数如下:

function Average(a,b:Double;c:Double=0.0):Double;overload; //求3 个实数平均值

从上例可以看出,尽管参数的个数与上面的两个不同,但第3 个参数设置了一个默认值,所以当
参数调用为语句Average(1.1,2.2);时,编译系统就不知道应该使用哪个重载函数了,因为第2 个重载函
数和第3 个重载函数都可以满足要求,这样就会出现一个编译错误。

5.函数的递归调用
在Object Pascal 中,过程或函数必须先说明再调用。以上规则在递归调用时属于例外情况。所谓递归调用,是指函数A 调用函数B,而函数B 又调用函数A 的情况,或是指一个函数调用自身的特殊情况。在递归调用中,函数要进行前置,即在函数或过程的标题部分最后加上保留字Forward。下文的例子是一个递归调用的典型例子:

program Project1;
{$APPTYPE CONSOLE}

var
    Alpha:Integer;

procedure Test2(var A:Integer);forward;

//Test2 被说明为前置过程
procedure Test1(var A:Integer);
begin
    A:=A-1;
    if A>0 then
        Test2(A); //经前置说明,调用未执行的过程Test2
        writeln(A);
end;

procedure Test2(var A:Integer); //经前置说明的Test2 的执行部分
begin
    A:=A div 2;
        if A>0 then
            Test1(A); //在Test2 中调用已执行的过程Test1
end;

begin
    Alpha := 15; //给Alpha 赋初值
    Test1(Alpha); //第1 次调用Test1,递归开始
end.

程序开始时给Alpha 赋初值,并实现先减1 再除2 的循环递归调用,直到Alpha 小于0 为止。

1.9 规范化命名

在系统开发的过程中,常常要为变量、类、对象、函数和文件等命名。一般在开发需求或设计阶段就必须制定出一套完整、实用的命名规则。这样,在很大程度上可以提高系统开发的效率,便于不同模块之间的接口,方便系统的维护。
在制定命名规则的时候,一个基本的原则就是便于使用、便于维护、风格统一。另外还应注意下面几点:

  • 命名时要采用英文单词,而不要使用中文拼音,尤其不要使用中文拼音第1 个字母的组合。在使用英文单词命名时,尽量采用统一、简单、贴切的词语,尽可能使用完整的单词或音节。
  • 有些名称可以采用几个英文单词的组合。在组合过程中,尽量不要使用下划线来分隔单词,最好采用大小写混写的方式来实现。
  • 对于保留字和指令字可以统一全部小写,而对于一些常量名可以全部大写。
  • 有些名称可以是“动词+对象”组合而成,也可以是“对象+动词”组合而成。一般来说,“动词+对象”比较符合平常的语法习惯。无论采用哪种方法,整体上都应该统一。
  • 在有些情况下,要考虑到与Delphi 集成开发环境的统一。例如在Delphi 集成开发环境中,普通类的名称一般以T 开头,异常类的名称一般以E 开头。
  • 在对菜单命令的标识号命名的时候,应将所属菜单项的名称包含进去。比如对于“文件”菜单项中的菜单命令,可以将标识号命名为FileOpen、FileClose 等。
  • 对于一些表示集合意义的名称,可以使用名词的复数形式。比如窗口的集合,可以使用Windows,而不要使用WindowCollection。

参考: http://chanlei001.blog.163.com/blog/static/3403066420117261154351

    http://www.cnblogs.com/del/archive/2007/12/04/982167.html

    http://blog.csdn.net/hudie1234567/article/details/6403419

Object Pascal 语法之语言基础(四)

时间: 2024-10-15 11:11:59

Object Pascal 语法之语言基础(四)的相关文章

Object Pascal 语法之语言基础(二)

1.5 数据类型与定义变量 Object Pascal 语言的最大特点是对数据类型的要求非常严谨.传递给过程或函数的参数值必须与形参的类型一致.在Object Pascal 语言中不会看到像C 语言编译器提示的“可疑的指针转换”等警告信息.由于Object Pascal 语言对数据类型比较严谨,因此它会对代码进行严格检查,以确保不会出现错误.变量是程序代码中代表一个内存地址的标识符,那么该地址的内存内容就可以在程序代码执行时被改变.每个变量都有一个名字和数据类型,名字可以用来引用变量,数据类型决

Object Pascal 语法之语言基础(一)

Delphi 是以Object Pascal 语言为基础的可视化开发工具,所以要学好Delphi,首先要掌握的就是Object Pascal 语言. 1 语言基础 Object Pascal 语言是在Pascal 语言的基础上发展起来的,它继承了Pascal 语言语法严谨.数据结构丰富等优点,同时融入了面向对象编程的语法要素,使之成为一个完善的面向对象的编程语言. 1.1 Object Pascal 语言编写环境 随着Windows 操作系统的普及,很少有人在DOS 环境下编写程序了,下面将为读

Object Pascal 语法之语言基础(三)

1.6 Object Pascal 的运算符 运算符是程序代码中对各种类型的数据进行计算的符号,通常分为算数运算符.逻辑运算符.比较运算符和按位运算符. 1.算术运算符Object Pascal 语言的算术运算符,如表1-9 所示.表1-9 Object Pascal 语言算术运算符 操作符 操作 操作数据类型 结果类型 + 加 整型.实型 整型.实型 - 减 整型.实型 整型.实型 * 乘 整型.实型 整型.实型 / 除 整型.实型 整型.实型 mod 取余 整型 整型 div 整除 整型 整

Object Pascal 语法之控制语句

控制语句 1.常量声明语句常量在声明时就被赋予了一个值,在程序执行过程中是不可改变的. 格式 const 常量名 :数据类型 = 值 下面的例子声明了3 个常量: const Pi = 3.14159; BookID = 362; BookName = "Delphi"; 像变量一样,常量也有类型.上文的3 个常量的类型分别是Real 型.整型.字符串型.常量用“=”表示两边的值是相等的. 为常量指定数据类型,声明方法如下: const  UserID :Integer =1; Use

C++语言学习(二)——C++对C语言基础语法的扩展

C++语言学习(二)--C++对C语言基础语法的扩展 C++是基于C语言扩展发展而来的面向对象的程序设计语言,本文将主要讨论C++语言基于C语言扩展的方面. 一.实用性增强 C语言中变量的定义必须在作用域开始的位置进行定义. #include <stdio.h> int main(int argc, char *argv[]) { int i;//定义变量 int j; //使用变量 for(i = 0; i < 10; i++) { for(j = 0; j < 10; j++)

嵌入式linux C++语言(二)——C++对C语言基础语法的扩展

嵌入式linux C++语言(二)--C++对C语言基础语法的扩展 C++是基于C语言扩展发展而来的面向对象的程序设计语言,本文将主要讨论C++语言基于C语言扩展的方面. 一.类型增强 1.类型检查更严格 在C语言中: const int a = 100; int *p = &a; 在C++语言中: const int a = 100;//必须在定义的时候初始化 const int *p = &a; 在C++语言中不能隐式转换数据类型. error: invalid conversion

终于懂了:Delphi的函数名不是地址,取地址必须遵守Object Pascal的语法(Delphi和C的类比:指针、字符串、函数指针、内存分配等)good

这点是与C语言不一样的地方,以前我一直都没有明白这一点,所以总是不明白:函数地址再取地址算怎么回事? ---------------------------------------------------------------------------------------------------------------- 在学习Delphi的时候,一个很好的建议是和C/C++去类比着学习,从指针,到内存管理,到数组,到面向对象……各个方面,都是有很多可以相似和或者也有不同的方,类比着学习,一

【OC语法快览】四、基础内存管理

Basic Memory Management                                                           基础内存管理 If you're writing an application for Mac OS X, you have the option to enable garbage collection. In general, this means that you don't have to think about memory

第五天:语言基础(四)

Java 中的运算符 一门编程语言,最常坐的工作就是数据运算.在 Java 中,有丰富的运算符来进行数值运算.这些运算符可以分为以下几类: 算数运算符 关系运算符 位运算符 逻辑运算符 赋值运算符 其它运算符 算数运算符 先来说说算数运算符. 操作符 描述 说明 + 加--运算符两侧的值进行相加 注意类型转换 - 减--左操作数减去右操作数 注意类型转换 * 乘--运算符两侧的值进行相乘 注意类型转换 / 除--左操作数除以右操作数 注意左右操作数类型的不同,结果的类型也不同 % 取余--左操作