第7章 过 程 本章要点: Sub过程 Function过程 参数传递
7.1 sub过程 7.1.1建立Sub过程 Visual Basic中根据过程是否有返回值,把过程分为两类: (2)Function过程:称为函数过程,返回一函数值。
过程分类: 内部函数 自定义函数 Function过程 内部过程(方法) 事件过程 自定义过程 Sub过程 其中,自定义函数和自定义过程统称为通用过程
事件过程 窗体事件过程的语法格式为: Private Sub Form_事件名(参数列表) <语句块> End Sub 控件事件过程的语法格式为: Private Sub 控件名_事件名(参数列表)
例如,下面是一个单击窗体的事件过程: Private Sub Form_Click( ) Print "这是一个VB的演示程序!" End Sub 例如,在窗体中设置了一个名为Command1的命令按钮控件,它对应的单击事件过程如下: Private Sub Command1_Click()
1.定义Sub过程 通用过程 通用过程又称自定义过程。若干不同的事件过程可能会执行相同的动作,可将共同语句独立出来置于一个过程中,这种过程称为通用过程。 通用过程的定义形式如下: [Private | Public] [Static] Sub 过程名([参数列表]) [局部变量和常量声明] <语句块> [Exit Sub] End Sub
说明: (1)以Private为前缀的Sub过程是窗体级的(私有的)过程,只能被本窗体模块内的事件过程或其它过程调用。 以Public为前缀的Sub过程是应用程序级的(公有的或全局的)过程。这样的过程可以从项目中的任何模块中调用它。若缺省Private 或 Public, 系统默认为Public 。 (2)Static选项:指定过程中的局部变量为“静态”变量。 (3)Sub过程以Sub语句开头,End Sub语句结束。在Sub和End Sub之间是描述过程操作的语句块,称为子程序体或过程体。 当程序执行到End Sub语句时,退出该过程,并立即返回到调用该过程语句的下一条语句。
(4)过程名的命名规则与变量命名规则相同。在同一个模块中,过程名必须唯一。 (5)参数列表中的参数称为形式参数,它可以是变量名或数组名。 Sub过程可以没有参数,但一对圆括号不可以省略,不含参数的过程称为无参过程。 形式参数的格式: [ByVal] [ByRef] 变量名[( )][As 数据类型]
说明: ByVal:表明其后的形参是按值传递参数或称为“传值”(Passed by Value)参数,若缺省或用“BvRef”替代,则表明参数是按地址传递(传址)参数或称为“引用”(Passed by Reference)参数。 变量名[( )]:变量名为合法的VB变量名或数组名。若变量名后无括号,则表示该形参是变量,否则是数组。
例7.1:编写一个求三角形面积的子过程: Private Sub Area (a As Single, b As Single,c As Single, s As Single) Dim p As Single p = (a + b + c) / 2 s = Sqr(p * (p - a) * (p - b) * (p - c)) End Sub
2.建立Sub过程 建立Sub过程有二种方法: 1.第一种方法: 在代码窗口直接按定义输入 2.第二种方法: (1)打开代码窗口;(2)选择“工具”菜单的“添加过程”;(3)输入过程名,选定过程类型,选择过程的应用范围 。
7.1.2调用Sub过程 要执行一个过程,必须调用该过程。子过程的调用有两种方式,一种是利用Call语句加以调用,另一种是把过程名作为一个语句来直接调用。 1.用Call语句调用Sub过程,格式如下: Call 过程名(实在参数表) 说明: (1) 执行Call语句,VB将控制传递给由“过程名”指定的Sub过程,开始执行它。 (2) 实在参数:是传送给被调用的Sub过程的变量,常数或表达式。实在参数的个数、类型和顺序,应与被调用过程的形式参数相匹配。有多个参数时各实在参数之间用逗号分隔。 如果被调用过程是一个无参过程,则括号可省略。
例如:调用例7.1求三角形面积的过程: Private Sub Command1_Click() Dim p As Single Call Area(5, 6, 7, p) Print "p="; p End Sub
2.把过程名作为一个语句来使用,格式如下: 过程名[实参数列表] 说明: 与第一种调用方法相比,这种调用方式省略了关键字Call,去掉了“参数列表”的括号。 例如:调用例7-1求三角形面积的过程: Private Sub Command1_Click() Dim p As Single Area 5, 6, 7, p Print "p="; p End Sub
7.2 Function过程 7.2.1 建立Function过程 [Public|Private][Static] Function <函数过程名> ([<形参表>]) [As <类型>] [<语句组>] <函数过程名> = <表达式> [Exit Function] [函数名=表达式] End Function
说明: (1)与Sub的区别:Function过程由函数名返回一个值。 (2) 函数名的命名规则:与变量名的命名规则相同,但不能与内部函数重名。 (3) As数据类型:指定函数返回值的类型。缺省该选项,函数类型默认为“Variant”类型。 (4) 在函数体内通过“函数名=表达式”语句给函数名赋值。 (5) Exit Function语句:提前退出Function过程返回调用点。
例7.2:编写一个求最大公约数(GCD)的函数过程。 Function gcd (ByVal x As Integer, ByVal y AsInteger) As Integer Do While y <> 0 reminder = x Mod y x = y y = reminder Loop gcd = x End Function
7.2.2调用Function过程 调用函数可以由函数名带回一个值给调用程序,被调用的函数必须作为表达式或表达式中的一部分,再与其它的语法成分一起配合使用。 最简单的情况就是在赋值语句中调用函数,其形式为: 变量名=函数名([实参数列表])
例7.3:利用例7-2中的Fact函数,求6!+8!+9!程序代码如下: Function fact1(n As Integer) As Double Dim val As Double, i As Integer val = 1 For i = 1 To n val = val * i Next i fact1 = val End Function 程序界面: 图7.3 用函数计算阶乘
例7. 4:编写程序,在文本框中显示出给定数值的阶乘值。 计算给定参数的阶乘值的通用过程如下: Private Function Factor(intInput As Integer) As Long Dim int1 As Integer Dim lngResult As Long int1 = 1 lngResult = 1 Do lngResult = lngResult * int1 int1 = int1 +1 Loop While int1 < intInput +1 Factor = lngResult End Function
下面的事件过程调用上述函数Factor来计算10!-9!的值: Private Sub cmd1_Click() txtOutput.Text = Factor(10) – Factor(9) End Sub
例7.5:从键盘上输入一个数,输出该数的平方根。 用内部函数Sqr可以得到一个数的平方根,但该数必须大于或等于0。如果是负数,则其平方根是一个虚数。我们用自己编写的过程来输出平方根。 程序如下:
Function SquareRoot (X As Double) As Double Select Case Sgn(X) Case 1 SquareRoot = Sqr(X) Exit Function Case 0 SquareRoot = 0 Case -1 SquareRoot = -1 End Select End Function Private Sub Form Click() Dim Msg,SqrN Dim N As Double N = InputBox(”请输入要计算平方根的数:”) Msg = N & ” 的平方根 ” SqrN = Square Root(N) Select Case SqrN Msg = Msg & ”是0” Case -l Msg = Msg & ”是一个虚数” Case Else Msg = Msg & ”是” & SqrN MsgBox Msg End Sub
过程SquareRoot用来求平方根。该函数有一个参数,其类型为Double,函数的返回值类型为Double。 在该过程中,用Sgn函数判断参数的符号,当参数为正数时,过程返回该参数的平方根,如果参数为0;则返回0值;如果参数为负数,则返回-1。在事件过程(主程序)中,用从键盘上输入的数调用SquareRoot过程,并根据返回的值进行不同的处理。
7.3 参数传递 参数传递是指调用过程与被调用过程之间的一种数据通讯方法。 7.3 参数传递 参数传递是指调用过程与被调用过程之间的一种数据通讯方法。 数据的传递方式可以是单向的,也可以是双向的。分别称为按值传递和按地址传递。 数据传递是通过形参和实参进行传递的。
7.3.1 形参和实参 形参: 出现在Sub或Function语句中。 实参: 出现在调用程序中。 例如,定义如下一个过程: 7.3.1 形参和实参 形参: 出现在Sub或Function语句中。 实参: 出现在调用程序中。 例如,定义如下一个过程: Sub SubTest(n As Integer,Sum As Single) … End Sub 调用语句: Call SubTest( a , s ) 形参 实参
说明: 一般情况下,形参表与实参表中对应参数个数、类型、位置顺序必须一一对应(除非使用关键字Optional或ParamArray对形参进行了约束) 形参表中的参数可以是: 除固定长度字符串之外的合法 变量名、数组名( ) 实参表中的参数可以是: 常量、变量、表达式、数组名( ) 形参与实参的结合有两种方式: 按值传递和按地址传递。
1.按值传递 如果调用语句中的实际参数是常量或表达式,或者定义过程时选用ByVal关键字就是按值传递。调用时,给形参分配一个临时内存单元,将实参的值复制到形参对应的内存单元中,实参与形参断开联系。调用结束后,返回主调过程时,形参对应的内存单元被释放。 实参的值不随形参的值变化而变化。如果在被调过程中形参值被改变,只影响临时内存单元,不影响实参变量本身。即当控制返回调用程序时,实参变量保持调用前的值不变。
按值传送参数的程序示例。 Private Sub Sum(ByVal X As Integer,ByVal Y As Integer) X = X + 20 Y = X + Y Print “X=”; X, “Y=”; Y End Sub Private Sub Form_Click() Dim M As Integer, N As Integer M = 10: N = 20 Call Sum(M, N) Print “M=”; M, “N=”; N
分析: 程序执行调用过程Sum时,采用按值传递参数,把实参M和N的值(10和20)分别传递给形参X和Y;执行过程Sum中的代码时,形参X和Y的值会发生变化,但M和N不会变化。所以程序运行结果是X=30,Y =50,M=10,N=20。
2.按地址传递 定义过程时形参前没有ByVal或有ByDef的为按地址传递参数。调用时,将实参变量的地址传递给形参,形参和实参共用同一个内存单元。 在被调过程中的形参值一旦被改变,相应的实参值也跟随着被改变了。即当控制返回调用程序时,实参变量值改变。即实参的值随形参的值变化而改变。
按地址传送参数的程序示例。 Private Sub Sum(X As Integer,Y As Integer) X = X + 20 Y = X + Y Print “X=”; X, “Y=”; Y End Sub Private Sub Form_Click() Dim M As Integer, N As Integer M = 10: N = 20 Call Sum(M, N) Print “M=”; M, “N=”; N
分析: 程序执行调用过程Sum时,采用按地址传递参数,把M的地址传给X,N的地址给Y,即M和X使用同一内存单元,N和Y也使用同一内存单元。X的值改变了,M的值也跟着变化,同理Y的值改变了,N的值也跟着改变,因为它们其实就是同一存贮单元。所以程序运行结果是X=30,Y =50,M=30,N=50。
7.4可选参数与可变参数 7.4.1 可选参数 在前面的例子中,一个过程中的形式参数是固定的,调用时提供的实参也是固定的。也就是说,如果一个过程有3个形参,则调用时必须按相同的顺序和类型提供3个实参。 在Visual Basic 6.0中,可以指定一个或多个参数作为可选参数。例如,假定建立了一个计算两个数的乘积的过程,它能可选择地乘以第三个数。在调用时,既可以给它传送两个参数,也可以给它传送3个参数。
为了定义带可选参数的过程,必须在参数表中使用Optional关键字,并在过程体中通过IsMissing函数测试调用时是否传送可选参数。 例如: Sub Multi (fir As Integer, sec As Integer, Optional third) n = fir * sec If Not IsMissing(third) Then n = n * third End If Print n End Sub
上述过程有3个参数,其中前两个参数与普通过程中的书写格式相同.最后一个参数没有指定类型(使用默认类型Variant),而是在前面加上了“Optional”,表明该参数是一个可选参数。在过程体中,首先计算前两个参数的乘积,并把结果赋给变量n,然后测试第三个参数是否存在,如果存在,则把第三个参数与前两个参数的乘积相乘,最后输出乘积(两个数或3个数)。
在调用上面的过程时,可以提供两个参数,也可以提供3个参数,都能得到正确的结果。 例如,如果用下面的事件过程调用: Private sub Form_click() Multi 10,20 End Sub 则结果为200;而如果用下面的过程调用 Multi 10,20,30 则结果为6000。
上面的过程只有一个可选参数,也可以有两个或多个。但应注意,可选参数必须放在参数表的最后,而且必须是Variant类型。可选参数过程通过Optional指定可选的参数,其类型必须是Variant,通过IsMissing函数测试是否向可选参数传送实参值。
Sub过程名(ParamAr ray数组名) 7.4.2可变参数 在C语言中,通常用预定义函数printf输出数据。用该函数可以输出一个数据,也可以输出任意多个数据。输出的数据就是函数的参数,因此printf是一个可变参数函数。在Visual Basic 6.0中,可以建立与printf类似的过程。 可变参数过程通过ParamArray命令来定义,一般格式为: Sub过程名(ParamAr ray数组名) 这里的“数组名”是一个形式参数,只有名字和括号,没有上下界。由于省略了变量类型,“数组”的类型默认为Variant。
前面建立的Multi过程可以求两个或3个数的乘积;下面定义的是一个可变参数过程.用这个过程可以求任意多个数的乘积。 Sub Multi ( ParamArray Numbers ( )) n = l For Each x In Numbers n = n * x Next x Print n End Sub 可以用任意个参数调用上述过程。例如: Private Sub Form_Click() Multi 2,3,4,5,6 输出结果为 720
由于可变参数过程中的参数是Variant类型,因此可以把任何类型的实参传送给该过程。 例如: Private Sub Form_Click() Dim a as integer,b As Long,c As Variant,d As Integer a = 6:b = 8 c = 12:d =2 multi a,b,c,d End Sub 和传统的程序设计语言一样,通用过程一般用变量作为形式参数。但是,和传统的程序设计语言不同,Visual Basic还允许用对象,即窗体或控件作为通用过程的参数。在有些情况下,这可以简化程序设计,提高效率。本节将介绍用窗体和控件作为通用过程参数的操作。
7.5 对象参数 7. 5.1 窗体参数 假定要设计一个含有多个窗体的程序,该程序有4个窗体,要求这4个窗体的位置、大小都相同。 7. 5.1 窗体参数 假定要设计一个含有多个窗体的程序,该程序有4个窗体,要求这4个窗体的位置、大小都相同。 窗体的大小和位置通过Left,Top,Width及Height属性来设置。 可以用窗体作为参数,编写一个通用过程: Sub FormSet(FormNum As Form) FormNum.left = 2000 FormNum. Top = 3000 FormNum. Width = 5000 FormNum. Height = 3000 End Sub
上述通用过程有一个形式参数,该参数的类型为窗体( Form)。在调用时,可以用窗体作为实参。例如: FormSet Form1 将按过程中给出的数值设置窗体Form1的大小和位置。 为了调用上面的通用过程,可以用“工程”菜单中的“添加窗体”命令建立4个窗体,即Forml,Form2,Form3和Form4。在默认情况下,第一个建立的窗体(这里是Form1)是启动窗体。
7.5.2 控件参数 和窗体参数一样,控件也可以作为通用过程的参数。即在一个通用过程中设置相同性质控件所需要的属性,然后用不同的控件调用此过程。
7.6 局部内存分配 在运行应用程序时,Visual Basic知道程序中有多少全局变量,并为它们分配内存。但是,Visual Basic不知道有多少局部变量,甚至不知道是否会调用程序中的某个过程。只有在调用一个过程时才建立该过程所包含的局部变量和参数,并为其分配内存,而在过程结束后清除这些局部变量。如果再次调用该过程,则重新建立这些变量。也就是说,局部变量的内存在需要时分配,释放后可以被其他过程的变量使用。
变量[()][As类型][,变量[()][As类型]]… 有时候,在过程结束时,可能不希望失去保存在局部变量中的值。如果把变量声明为全局变量或模块级变量,则可解决这个问题。但如果声明的变量只在一个过程中使用,则这种方法并不好。为此,Visual Basic提供了一个Static语句,其格式如下: Static 变量表 其中“变量表”的格式如下: 变量[()][As类型][,变量[()][As类型]]…
可以看出,Static语句的格式与Dim语句完全一样,但Static语句只能出现在事件过程、Sub过程或Function过程中。在过程中的Static变量只有局部的作用域,即只在本过程中可见,但可以和模块级变量一样,即使过程结束后,其值仍能保留。 编写一个裁判评分程序,设共有裁判六人,评分满分为10分,除去一个最高分再除去一个最低分,剩余的评分的平均值即为选手得分。 分析:裁判的评分通过文本框输入,所以要在窗体建立具有六个元素的文本框控件数组Text1(5),最后得分通过文本框Text2显示。在窗体上还要建立二个命令按钮,一个是计算得分,一个是清除分数。
Private Sub Sort(AA() As Single, Av As Single) Option Explicit Option Base 1 Private Sub Command1_Click() Dim A(6) As Single, I As Integer, Aver As Single For I = 1 To 6 ‘把文本框中的分数存入数组A A(I) = Text1(I - 1) Next I Call Sort(A, Aver) Text2.Text = Aver ‘在Text2中显示最后得分 End Sub Private Sub Sort(AA() As Single, Av As Single) Dim I As Integer, J As Integer, temp As Single Dim sum As Single For I = 1 To UBound(AA) - 1 For J = I + 1 To UBound(AA) If AA(I) > AA(J) Then ‘由高到低对数组中的数据排序 temp = AA(I) AA(I) = AA(J) AA(J) = temp End If Next J For I = 2 To UBound(AA) – 1 ‘去掉第一个和最后一个数再求和 sum = sum + AA(I) Av = round(sum / 4,2) ‘最后得分保留二位小数
Private Sub Command2_Click() Dim A(6) As Single, I As Integer, Aver As Single For I = 1 To 6 Text1(I - 1) = "" ‘清除分数 Next I Text2.Text = "" ‘清除最后得分 End Sub
利用函数过程编写一个求两个正整数的最大公约数的程序。 分析:求两个正整数的最大公约数的方法可以采用辗转相除法,算法描述如下: 分别用m,n,r表示被除数、除数、余数 ①若m<n,则交换m和n 的值 ②求m除以n的余数r。 ③若r=0,则n为最大公约数,结束。若r≠0,执行第③步。 ④将n的值放在m中,将r的值放在n中。 ⑤返回重新执行第②步。
Private Sub Form_Click() ' 主调过程 Dim N As Integer, M As Integer, G As Integer N = InputBox("输入N") M = InputBox("输入M") G = Gcd(N, M) Print N; "和"; M; "的最大公约数是:"; G End Sub Private Function Gcd(ByVal A As Integer, ByVal B As Integer) As Integer Dim R As Integer R = A Mod B Do While R <> 0 A = B B = R Loop Gcd = B End Function
本章小结 Sub过程 定义: [Private | Public ] [Static] Sub <过程名>([<形参表>]) …… End Sub 调用: (1)Call <过程名>([<实参表>]) (2)<过程名> [<实参表>] 注意: Sub过程调用与函数过程定义与调用的区别
2. Function过程 定义: [Public | Private ] [Static] Function <函数名> [(<形参表>)] [As <类型>] …… [函数名 =表达式] End Function 形参表:变量、数组名() 调 用: 函数名(<实参表>) 出现在表达式可以出现的位置
3. 参数传递 按值传递: 实参为常数、表达式; 实参为变量,形参为 ByVal 变量 按地址传递: 实参为变量、数组;形参用 ByRef 定义的变量、数组(或省略ByRef) 使用可选参数(形参用Optional) 使用可变参数(形参用ParamArray) 使用对象参数(形参定义为Form、Control)