Visual Basic6.0函数与过程[下学期]

文档属性

名称 Visual Basic6.0函数与过程[下学期]
格式 rar
文件大小 257.9KB
资源类型 教案
版本资源 通用版
科目 信息技术(信息科技)
更新时间 2006-03-31 11:12:00

图片预览

文档简介

(共216张PPT)
第四章 函数与过程
本章将全面介绍Visual Basic函数、过程、文件,其主要内容有:
函数概述
过程概述
过程之间参数的传递
过程的嵌套调用和递归调用
过程的作用域
文件的操作
4.1 函数概述
在Visual Basic中,函数是一段用来表示完成某种特定运算或功能的程序。与数学中使用的函数相似,一旦给定函数名及其参数(即数学中的自变量)调用该函数,就会得到相应的函数值。而这种函数调用的过程,其实质就是执行与其对应的程序段的过程。
通常情况下,函数用在表达式中,但有些函数还可以和语句一样使用(如MsgBox),这是由于Visual Basic在提供这种函数的同时,还提供了相同的过程(语句),它们的差别在于:函数调用返回一个函数值,过程调用则没有返回值。
调用函数的一般格式如下:
函数名([参数表])
其中,函数名不区分大小写且是必不可少的,而函数的参数在个数、顺序、类型以及取值范围(定义域)上,不同的函数有不同的规定,使用时一定要注意。没有参数的函数称为无参函数(函数名后的括号可以省略不写),有参数的函数称为有参函数。参数可以是变量、常量和表达式。当参数个数多于一个时,其间用逗号分隔。
函数
标准函数
用户自定义函数
4.1.1 自定义函数
自定义函数是用户根据需要自己定义的函数。与标准函数不同,实现自定义函数功能的程序段必须由用户自己按照Visual Basic的要求的方式编制,自定义函数定义好之后,其调用方法与标准函数一样。自定义函数的定义和调用参见4.2.2节。
4.1.2 标准函数
标准函数也称内部函数或库函数,是Visual Basic系统为实现特定功能而设置的内部程序,用户不用再进行定义,可以直接使用。在程序解释执行或编译成可执行文件时,这些内部的定义(实际上是相关的计算机指令)会被执行或连接进可执行文件中。
Visual Basic提供了大量的内部函数。在这些函数中,有些是通用的,有些则与某种操作有关。标准函数按其功能和用途,可以分为数学函数、转换函数、字符串函数、时间和日期函数、格式函数和测试函数等。
数学函数
数学函数用于各种数学运算,包括三角函数、绝对值函数、平方根函数、对数函数,指数函数等。该类函数的参数和返回值都是数值型的。常见的数学函数见表4-1。
表4-1 常见的数学函数
函数名及调用格式
功 能
返回值类型
Sin(x)
正弦函数
Double
Cos(x)
余弦函数
Double
Tan(x)
正切函数
Double
Atn(x)
反正切函数
Double
Abs(x)
取绝对值
与x同型
Sgn(x)
符号函数
I nteger
Sqrt(x)
平方根函数(x必须大于等于0)
Double
Exp(x)
以e为底的指数函数,即ex
Double
Log(x)
以e为底的自然对数函数
Double
Rnd([x])
产生0~1(包括0但不包括1)之间的随机数
Double
说明:
(1) 三角函数Sin,Cos和Tan的自变量是以弧度为单位, Atn函数的自变量是正切值,它返回正切值为x的以弧度为单位的角度。例如:Sin45°应写成:Sin(45*3.14159/180) 。角度转换为弧度的计算公式为: 1°=π/180=3.141 59/180(弧度)
(2) 符号函数Sgn(x),根据x的值返回一个整数。即当x>0时,返回1;当x=0时,返回0;当x<0时,返回-1。
(3) Log(x)函数返回自变量x以e为底的自然对数。求以10为底的常用对数时,可以用如下换底公式:
(4) 随机函数Rnd的自变量可以省略不写。利用该函数可以产生给定区间 [n,m] 上的随机数,其公式为:
Int((m-n+1)*Rnd+n)
在调用 Rnd 之前,先使用无参数的 Randomize 语句初始化随机数生成器,该生成器具有根据系统计时器得到的种子。
随机函数Rnd的自变量的值决定了 Rnd 生成随机数的方式。当自变量:
小于0:每次都使用自变量的值作为随机数种子得到的相同结果;
大于0或省略:返回序列中的下一个随机数;
等于0:最近生成的数;
(5) 用户可以用两种方法验证标准函数(推荐使用第二种)。
将要验证每个函数写入事件过程,通过执行事件过程查看结果。
通过立即窗口验证标准函数。例如,在立即窗口中输入:
? Abs(-23) (为回车键)
23
2、转换函数 
转换函数主要用于类型和形式的转换,包括整型、浮点型、字符串、数值与ASCII字符之间的转换。常用的转换函数见表4-2。
函数名及调用格式
功 能
返回值类型
Int(x)
得到不大于x的最大整数
Integer
Fix(x)
截掉小数部分,只保留整数部分
Integer
CInt(x)
将x的小数部分四舍五入转换成整数
Integer
CLng(x)
将x的小数部分四舍五入转换成长整型数
Long
CDbl(x)
将x转换为双精度型数
Double
CSng(x)
将x转换为单精度型数
Single
Cvar(x)
将x转换为变体类型值
Variant
Ccur(x)
将x转换为货币类型值
Currency
Cbool(x)
按x的值转换为逻辑型数据
Boolean
表4-2 常用的转换函数
表4-2 常用的转换函数(续表)
Cbyte(x)
按x的值转换为字节型数据
Byte
Cdate(x)
将x转为日期型
Date
CStr($x)
将数值x转为字符串
String
Hex$(x)
将十进制数x转换为十六进制
String
Oct$(x)
将十进制数x转换为八进制
String
Asc(x$)
求字符串x$中第一个字符的ASCII码
Integer
Chr$(x)
求ASCII码值为x的字符
String
Val(x$)
将字符串x$转换为数值
Double
Str$(x)
将数值转换为字符串
String
LCase$(x$)
将字符串x$转换为小写
String
Ucase$(x$)
将字符串x$转换为大写
String
函数名及调用格式
功 能
返回值类型
说明:
(1) 注意区分Int(x)、Fix(x)和Cint(x)三个函数在使用上的差别。Fix(x)为截断取整,Int(x)的值为不大于x的最大整数,即当x>0时,Int(x) = Fix(x);当x<0时,Int(x) = Fix(x)-1;故Fix(x)=Sgn(x) * Int(Abs(x))。CInt(x)是四舍五入取整,但如果小数部分正好是0.5,则按靠偶的方向取整。例如:
Int(6.59) 结果为6
Cint(6.59) 结果为7
Int(-6.59) 结果为-7
Cint(6.49) 结果为6
Fix(6.59) 结果为6
Cint(7.5) 结果为8
Fix(-6.59) 结果为-6
Cint(8.5) 结果为8
(2) 使用Int函数可以将一个实数x保留n位小数,其公式为:
Int(x*10^n+0.5)/10^n

(3) Ccur(x)将x值转换为货币型值,小数部分最多保留四位且自动四舍五入。例如:
ccur(12345.6754721) 结果为 12345.6755

(4) Cbool(x)函数按表达式的值返回逻辑型数据。当表达式的值为逻辑型数据时,该函数按表达式的值返回Ture或False;当表达式的值为数值型数据时,值非零时返回Ture,值为零时返回False。例如:
Cbool(5>10) 结果为False
Cbool(0.5) 结果为True
(5) Cdate(x) 将x的值转换为日期型数据。X可以是一个日期型表达式、字符串表达式或数值表达式。当X为数值时,整数部分转化为日期,小数部分转化为从午夜算起的时间。转化得到的日期格式取决于系统上的区域设置。如果提供的格式不能识别,则无法判断年、月、日的顺序;长格式中,如果包含有星期字符串,也不能识别。例如:
Cdate("February 25,98") 结果为98-2-25
Cdate("5:35:40 PM") 结果为17:35:40

(6) Val(x$)函数将字符串型数据转换为数值型数据。转换时,自动去掉字符串中的空格、制表符和换行符。该函数还可识别进位制符号&O(八进制)和&H(十六进制)。当遇到非数字字符时即停止转换。例如:
Val("123xyz") 结果为123
Val(" 123 89") 结果为12389
Val("23.98") 结果为23.89
(7) Str$(x)函数将数值型数据转换为字符串。字符串的第一位一定是空格(x>0)或负号(x<0),小数点后0将去掉。例如:
Str(123) 结果为” 123”
Str(-123.45) 结果为”-123.45”
Str(-123.450000) 结果为”-123.45”
3、字符串函数 
在计算机的各种应用中,有大量的文字处理操作,如字符串的查找、比较、截取等。Visual Basic提供了大量的字符串函数,给编程中的字符处理带来了极大方便。字符串函数见表4-3。
表4-3 字符串函数
函数名及调用格式
功 能
LTrim(x$)
取掉字符串左面的空格
RTrim(x$)
取掉字符串右面的空格
Trim(x$)
取掉字符串两边的空格
Left$(x$,n)
取字符串x$最左边的n个字符
Right$(x$,n)
取字符串x$最右边的n个字符
Mid$(x$,n[,m])
从字符串x$的第n个字符开始,向后取m个字符。
String$(n,x$)
或String$(n,m)
返回由字符串x$的第一个字符或ASCII码值为m的对应字符组成的n个相同字符构成的字符串
Space$(n)
产生由n个空格构成的字符串
表4-3 字符串函数(续表)
Spc(n)
与Print #语句或Print方法配合使用,对输出进行定位
InStr([n,]x1$,x2$[,m])
求字符串x2$在字符串x1$中最早出现的位置
Len(x$)
或Len(变量名)
返回字符串x$的长度
返回存储某类变量所需要的存储空间
StrComp(x1$,x2$,[m])
以-1、0、1分别表示字符串x1$<、=、> 字符串x2$
StrReverse(x$)
返回字符串x$的反序
StrConv(x$,n)
根据n值完成字符串在ANSI与UniCode之间转换
说明:
(1) 在Visual Basic6.0中,字符串长度以字为单位,即每个西文字符和每个汉字一样,都为两个字节,这与传统的ANSI方式(西文采用一字节编码,汉字采用两字节编码)不同,其原因是Visual Basic 4.0之后,对西文字符和汉字采用了一种称为UniCode方式(统一编码方式)的编码方案,在此方案中,不分中西文均采用两字节编码方式。因此,象字符串“BASIC语言程序设计”,在UniCode方式下其长度为11,而在ANSI方式
下长度为17。在Visual Basic6.0中,为兼容ANSI方式,新增了一组函数,这些函数和原有的字符串函数相对应,在原有函数名的后面增加了一个字母B,如:LenB、RightB、MidB等等。例如:
Len("BASIC语言程序设计") 结果为11
LenB("BASIC语言程序设计") 结果为17
Mid$(“程序设计”,3,2) 结果为"设计"
MidB$(“程序设计”,3,2) 结果为"序"
(2) 可以用StrConv函数实现ANSI与UniCode之间的转换。其引用格式如下:
新字符串=StrConv(待转换字符串,格式字)
其中,待转换字符串可以是字符串常量或变量,格式字用来指定要转换成何种格式,当格式字为:
vbUniCode(值为64) ANSI格式→UniCode格式
vbFromUniCode(值为128) UniCode格式→ANSI格式
注意:当把字符串转换为ANSI格式后,必须用加了B的字符串函数进行处理;当字符串需要输出或保存时,必须转换为UniCode格式,否则将出错。例如:
?StrConv("BASIC语言程序设计",vbFromUniCode)
将输出一串?,表示出错
(3) 为了便于区分,字符串函数带有“$”。在Visual Basic中,“$”可以省略。(实际上,带有“$”时,返回字符型,否则返回Variant型,但这种差别实际上没有多大意义。)
(4) Mid$(x$,n[,m])函数中的参数m省略时,其返回值为由n指定位置开始的所有字符串。如果n的值大于字符串的长度,则返回值为空串。
注意:该函数还有一特殊用途,即
Mid$(x$,n[,m])=子字符串
其作用是把x$中从第n个字符开始的字符用“子字符串”替代。如果含有m参数,则替换的内容是“子字符串”左边的m个字符。
(5) Left$(x$,n)、Right$(x$,n)函数,当n=0时,返回值均为空串;当n≥Len(x$)时,返回值为整个字符串。

(6) InStr([n,]x1$,x2$[,m])函数中,n用来设定每次检索的起点,当n为1时可省略。参数m是一整型数,用来指定字符串的比较方法,当
m=0 按二进制进行比较,区分字母大小写。
m=1 比较时忽略大小写。
m=2 基于数据库中包含的信息进行比较(只适用于Microsoft Access)。
也可以通过Option Compare语句限定,具体如下:
Option Compare Binary 按二进制进行比较,区分字母大小写。
Option Compare Text 只比较字符的文本内容,不区分字母大小写。
Option Compare Database 对数据库中的信息进行比较。
当m和Option Compare语句均省略时,默认的比较方式为区分大小写。
InStr函数的返回值为一长整型数,各参数的取值不同时,其结果也不同。即:
当x1$为零长度时,或x2$在x1$中找不到,则返回值为0。
如果x1$为Null,或x2$为Null时,返回值也为Null。
例如:
InStr(1,"XYZ","AB") 结果为0,即没有找到子串
InStr(1,"XYZ","yz",1) 结果为3。

(7) StrComp(x1$,x2$,[m])函数用于比较两个字符串之间的关系,其结果用数值-1,0,1表示。即当
x1$x1$=x2$ 返回值为0
x1$>x2$ 返回值为1
参数m的作用与InStr([n,]x1$,x2$[,m])函数中的m相同。
(8) StrReverse(x$)函数的作用是返回字符串x$的反序。如果x$是零长度字符串 (""),则返回零长度字符串。如果x$为 Null,则会出现错误。例如:
StrReverse("12345") 结果为"54321"
4、日期、时间函数 
Visual Basic提供了丰富的日期、时间函数,利用这些函数可以提取系统的日期、时间。可截取年、月、日、时、分、秒,还可进行日期和时间的计算。日期函数见表4-4。
表4-4 日期函数
函数名及调用格式
功 能
返回值类型
Date$[( )]
返回当前系统日期
Date
Year(date)
返回年份
Integer
Month(date)
返回月份
Integer
表4-4 日期函数(续)
Day(date)
返回日期
Integer
now
返回计算机系统的当前日期和时间值。
Date
Time$[( )]
返回系统当前的时间
Date
Hour(time)
返回指定时间的小时数
Integer
Minute(time)
返回指定时间的分钟数
Integer
Second(time)
返回指定时间的秒数
Integer
Timer[( )]
返回从午夜开始到现在经过的秒数
Single
Weekday(date, [firstdayofweek])
返回指定日期的星期代号,星期日为1…
Integer
TimeValue(time)
将时间字符串转换为时间
Date
DateValue(date)
将日期字符串转换为日期
Date
TimeSerial(hour, minute, second)
将指定的时分秒转换为时间
Date
DateSerial(year, month, day)
将指定的年月日转换为日期型的表达式
Date

表4-4 日期函数(续)
说明:
(1) 表中参数date和time,可以是任何能够表示日期和时间的Variant、数值表达式、字符串表达式或它们的组合。如果 date或time 包含 Null,则返回 Null。

(2) Now是一个内部函数,不需要用户定义。用它作为自变量,可以用日期和时间函数返回当前系统的日期和时间。

(3) 使用Date$函数可以以“yyyy-mm-dd”的格式显示系统的当前时间。例如:
date$
2004-10-15
而使用Date$ 语句来设置当前系统日期。其格式为:
Date$=日期字符串
日期字符串的格式可以是“mm-dd-yy”或“mm-dd-yyyy”。例如:
Dim MyDate
MyDate = #February 12, 1985# ' 指定某个日期。
Date = MyDate ' 改变系统日期。
Now与函数Format$配合使用,可以按指定的格式显示当前日期。例如:
?Format$(now,"mm-dd-yyyy")
10-16-2004
?Format$(now,"mm-dd-yy")
10-16-04
?Format$(now,"dd-mm-yyyy")
16-10-2004
?Format$(now,"yyyy-mm-dd hh:mm:ss")
2004-10-16 17:01:16
(4) 与Date$函数相似,可以用Time$函数以“hh:mm:ss”格式返回系统当前的时间值。例如:
Time$
0:13:59
而使用Time$ 语句则可以来设置系统时间。其格式为:
Time = 时间字符串
时间字符串的格式“hh:mm:ss”。Time 语句会根据时间字符串指定的时间,利用时间分隔符将其转换成一个时间。如果无法转换成一个有效的时间,则会导致错误发生。例如:
Dim MyTime
MyTime = #4:35:17 PM# ' 指定一时间。
Time = MyTime ' 将系统时间设置为 MyTime 指定的内容。

(5)Weekday(date, [firstdayofweek])函数返回指定日期的星期代号,在省略firstdayofweek参数的情况下,其返回值:星期日为1,…,星期六为7。
firstdayofweek 参数的值见表4-5。
表4-5 firstdayofweek 参数的值
内部常数

描述
内部常数

描述
vbUseSystem
0
使用NLS API设置
vbWednesday
4
星期三
VbSunday
1
星期日(默认值)
vbThursday
5
星期四
vbMonday
2
星期一
vbFriday
6
星期五
vbTuesday
3
星期二
vbSaturday
7
星期六
(6)TimeValue(time) 和DateValue(date)可用来分别返回时间和日期的序数值或相应的时间和日期。
DateValue 就会根据系统中指定的短日期格式来识别月、日、年的顺序。DateValue 也识别明确的英文月份名称,全名或缩写均可。
使用DateValue(date)函数时,调用方式方法不同,其结果也不同。即:
① 若以
Print DateValue(date)
方式调用DateValue(date)函数,则将日期字符串转换为日期。例如:
DateValue("February 12, 1969")
1969-2-12
DateValue("1969-2-12")
1969-2-12
② 若以
a#= DateValue(date)
Print a#
方式调用DateValue(date)函数,则返回值以1899年12月30日为基准(0),向前为负值,向后为正值,有效日期为100 年 1 月1 日到 9999 年12 月31 日。
例如:
a#=DateValue("12-30-1899"): a#
0
a#=DateValue("12-29-1899"): a#
-1
a#=DateValue("12-31-1899"): a#
1
a#=DateValue("1-1-100"): a#
-657434
a#=DateValue("12-31-9999"): a#
2958465
a#=DateValue("February 12, 1969") : a#
25246
a#=DateValue(now) : a#
38276
Timevalue(time)函数的参数time可以使用12小时制或24小时制的时间格式。取值范围为0:00:00~23:59:59。例如,”2:24PM”和"14:24"均是有效的time表达式。其调用格式与DateValue(date)函数相似,按上述格式①调用时,其结果是将时间字符串转换为时间。例如:
timevalue("00:00:00"),timevalue("23:59:59"),timevalue("12:00:00"),TimeValue("4:35:17 PM")
0:00:00 23:59:59 12:00:00 16:35:17
当timevalue(time)函数按上述格式②调用时,其结果是将时间字符串转换为0~.999988425925926的一个值。例如:
t#=timevalue("00:00:00"): t#
0
t#=timevalue("23:59:59"): t#
.999988425925926
t#=timevalue("12:00:00"): t#
.5
(7) TimeSerial(hour, minute, second) 和 DateSerial(year, month, day)函数:利用这两个函数,可实现日期或时间的运算。当任何一个参数的取值超出正常范围时,它会适时进位到下一个较大的时间单位。例如,如果指定了 75(75 分钟),则这个时间被解释成一小时又十五分。如果一个参数值超出 -32,768 到 32,767 的范围,就会导致错误发生。如果三个参数指定的时间会使日期超出可接受的日期范围,则亦会导致错误发生。
与调用TimeValue(time) 和DateValue(date)函数相似,如果以
Print TimeSerial(hour, minute, second)
或 Print DateSerial(year, month, day)
方式调用此函数,则两函数将分别求出三个自变量的值,并依次化简后连接起来,返回相应的时间或日期型数据。例如:
dateserial(2004,10,55)
2004-11-24
TimeSerial(16, 75, 17)
17:15:17

若以
a#= TimeSerial(hour, minute, second) (或a#= DateSerial(year, month, day))
Print a#
方式调用TimeValue(time) 和DateValue(date)函数,对于前者,返回值在0~.999988425925926范围内;对于后者,则返回值以1899年12月30日为基准(0),向前为负值,向后为正值,有效日期为100 年 1 月1 日到 9999 年12 月31 日。例如:
d#=dateserial(2004,10,16): d#
38276
d#=dateserial(1899,12,30): d#
0
t#=TimeSerial(16,75,17): t# ' TimeSerial(16,75,17)相当于TimeSerial(17,15,17)
.718946759259259
t#=TimeSerial(0,0,0): t#
0
t#=TimeSerial(12,0,0): t#
.5
t#=TimeSerial(23,59,59): t#
.999988425925926

另外,利用TimeValue(time) 和DateValue(date)函数可以很方便的求出某个时刻(或某个日期)之前(或之后)若干时间前后的时间或日期。例如:
TimeSerial(12 - 6, -15, 0)
5:45:00
即中午之前六小时(12-6)又十五分钟(-15)的时间为5:45:00 A.M.
DateSerial(1990-10,8-2,1-1)
1980-5-31
即1990年8月1日的十年(1990-10)零两个月(8-2)又一天(1-1)之前的日期就是1980年5月31日。
5、格式输出函数 
格式输出函数Format$可以用来指定数值、字符串、日期、时间等数据的输出格式,广泛用于程序设计中的数据输出控制。其基本格式如下:
Format(expression[, format[, firstdayofweek[, firstweekofyear]]])
说明:
(1) 该函数的基本功能是按参数“format”指定的格式输出“expression”的值。其中,各个参数的含义如下:
expression参数,必选项。可以是任何有效的表达式。
format参数,可选项,可以是有效的命名表达式或用户自定义格式表达式,用来规定Format函数的返回值格式。当格式字符串为常量时,必须放在双引号中。数据项的显示格式(包括显示区段的长度)就是由这些格式字符决定。
firstdayofweek参数,可选项。它是一个常数,取值范围为0~7。表示一星期的第一天是星期几。其具体含义参见前面介绍的WeekDay函数。
firstweekofyear参数,可选项。它也是一个常数,取值范围为0~3。表示一年的第一周如何确定。其具体含义见表4-6。
表4-6 表示一年第一周如何确定的常数
内部常数

说 明
vbUseSystem
0
使用 NLS API 设置。
vbFirstJan1
1
从包含一月一日的那一周开始(默认)。
vbFirstFourDays
2
从本年第一周开始,而此周至少有四天在本年中。
VbFirstFullWeek
3
从本年第一周开始,而此周完全在本年中。
(2) 使用Format函数时,如果省略了可选项参数,则系统使用默认值。在对参数expression的内容进行输出控制时,可根据要达到的格式化效果来决定如何设置。格式控制既可以使用预先定义的命名格式,也可以使用用户自定义格式。
(3) 如果省略格式字符串format,则Format函数的功能与Str函数基本相同。惟一的差别是,当把正数转换成字符串时,Str函数在字符串前面留有一个空格,而Format函数则不留空格。
(4) 格式输出函数返回值是字符串,它一般用于Print方法中。
(5) 数值型数据的格式化可按表4-7给出的格式符进行定义。
格式
字符
作 用
#
数字占位符,一个#表示一个数字位。#的个数决定了显示区段的长度。如果要显示的数值的位数小于格式字符串指定的区段长度,则该数值靠区段的左端显示,多余的位不补0。如果要显示的数值的位数大于指定的区段长度,则数值照原样显示。
0
数字占位符,与#功能相同,只是多余的位用0补齐。
.
小数点占位符,用来显示小数点。小数点与#或0配合使用,可以写在显示区段的任何位置。按格式字符串的要求,小数部分多余的数字作四舍五入处理。

千分位符号占位符,在格式字符串中插入逗号,起到“分位”的作用,即从小数点左边一位开始,每3位用一个逗号自动分开。逗号可以写在小数点左边除头部和尾部的任何位置,如果写在小数点左边的头部或尾部,则不能得到正确的结果。
%
百分比符号占位符,数字乘以100并在右边加上百分号(%)。
表4-7 数值格式符及其作用
表4-7 数值格式符及其作用(续)
格式
字符
作 用
-+$
这些字符可以原样显示。如想显示那些列出的字符之外的字符时,可以用反斜杠 ( \ ) 字符作前缀或以双引号 (" ") 括起来。
E+E-
e- e+
E+(E-)用指数形式(科学记数法格式)显示数值。两者作用基本相同。
\
将格式字符串中的下一个字符原样显示出来。反斜杠本身并不会显示出来。而使用反斜杠 (\) 的效果和使用双引号是一样的。如想显示反斜杠字符,可使用两个反斜杠 (\\)。
那些不能显示为原义字符的字符是日期格式字符和时间格式字符(a, c, d, h, m, n, p, q, s, t, w, y, / 和 :)、数值格式字符(#, 0, %, E, e, 逗点和句点)和字符串格式字符(@, &, <, > 和 !)。
("ABC")
显示双引号 (" ") 之内的字符串。如在代码中想在 format 中包含一个字符串,必须用 Chr(34) 将文本括起来(34 为双引号 (") 的字符代码)。
注:对于字符“0”和“#”,如果要显示的数值表达式的小数部分位数多于格式字符串的位数,按四舍五入返回。

(6) 日期和时间数据格式化可按表4-8给出的格式符进行定义。
表4-8 日期和时间数据格式符及其作用
格式
字符
作 用

时间分隔符。用来分隔时、分和秒。时间分隔符的真正字符在格式输出时,取决于系统的设置。
/
日期分隔符。用来分隔年、月、日。日期分隔符的真正字符在格式输出时,取决于系统设置。
c
以ddddd格式显示日期,以ttttt格式显示时间
d
以没有前导的数字显示日(1~31)
dd
以有前导的数字显示日(1~31)
ddd
三字符星期缩写(sun~sat)
dddd
星期全名
ddddd
按系统的短日期格式显示完整的日期
dddddd
按系统的长日期格式显示完整的日期
格式
字符
作 用
w
将星期日~星期六用数字1~7表示
ww
将一年中的星期用数值表示(1~54)
m
如果不直接跟在h或hh后面,以没有前导的数字显示月(1~12)
mm
如果不直接跟在h或hh后面,以有前导的数字显示月(01~12)
mmm
以简写来表示月(Jan~Dec)
mmmm
以全称来表示月(January~December)
q
将一年中的四季用数值来表示(1~4)
y
将一年中的日用数值来表示(1~366)
yy
用两位数来表示年(00~99)
yyyy
用四位数来表示年(0000~9999)
h
以没有前导的数字显示小时(0~23)
hh
以有前导的数字显示小时(00~23)
表4-8 日期和时间数据格式符及其作用(续)
表4-8 日期和时间数据格式符及其作用(续)
n
以没有前导的数字显示分钟(0~59)
nn
以有前导的数字显示分钟(00~59)
s
以没有前导的数字显示秒(0~59)
ss
以有前导的数字显示秒(00~59)
t
一位字母缩写AM/PM (例如: "AM" 由 "A" 来代表)。
tt
两位字母缩写AM/PM (例如: "AM" 由 "AM" 来代表)。
ttttt
以完整时间格式显示(包括时、分、秒)
AM/PM
以大写AM和PM符号来分别表示午前和午后的时间
am/pm
以小写am和pm符号来分别表示午前和午后的时间
A/P
以大写A和P符号来分别表示午前和午后的时间
a/p
以小写a和p符号来分别表示午前和午后的时间
AMPM
按系统设置的AM或PM字符串来分别表示午前和午后的时间
注:在对日期和时间数据进行格式化输出时,可对表中给出的格式字符进行有机组合,以形成需要的输出控制格式。

例如:
d-mmmm-yy 按“日-月份全名-年”格式输出
m/d/yy h:mm 按“月/日/年 小时:分”格式输出
h:mm:ss a/p 按“小时:分:秒 a或p”格式输出
mmmm-yy 按“月份全名- 年”格式输出

(7) 字符串数据格式化可按表4-9给出的格式符进行定义。
表4-9 字符串数据格式符及其作用
格式字符
作 用
@
字符占位符。显示字符或是空白。如果字符串在格式字符串中 @的位置有字符存在,那么就显示出来;否则,就在那个位置上显示空白。除非有惊叹号字符 (!) 在格式字符串中,否则字符占位符将由右而左被填充。
&
字符占位符。显示字符或什么都不显示。如果字符串在格式字符串中和号 (&) 的位置有字符存在,那么就显示出来;否则,就什么都不显示。除非有惊叹号字符 (!) 在格式字符串中,否则字符占位符将由右而左被填充。
<
强制小写。将所有字符以小写格式显示。
>
强制大写。将所有字符以大写格式显示。
!
强制由左而右填充字符占位符。默认值是由右而左填充字符占位符。
(8) Visual Basic 提供了几种与 Format 函数一起使用的标准格式。在 Format 函数的 format 参数中,可使用名字来指定这些格式,而不用在 format 参数中指定符号。格式名总是用双引号 ("") 括起来。这些标准格式包括数值格式(又称预定义数值格式)、日期和时间格式(又称预定义日期和时间格式),分别见表4-10、表4-11。
表4-10 预定义数值格式名称及其作用
数值格式名称
作 用
General Number
显示没有千位分隔符的数字。
Currency
显示带千位分隔符的数字;在小数点的右边显示两位数字。而输出则依据用户的系统设置。
Fixed
在小数点的左边至少显示一位数字,在小数点的右边显示两位数字。
Standard
显示带千位分隔符的数字,在分隔符的左边至少显示一位数字,而在分隔符的右边至少显示两位。
数值格式名称
作 用
Percent
该值乘以 100,在后面加上一个百分号。
Scientific
用标准的科学记数法。
Yes/No
任何非零数字值(通常是 -1)为 Yes,零为 No。
True/False
任何非零数字值(通常是 -1)为 True,零为 False。
On/Off
任何非零数字值(通常是 -1)为 On,零为 Off。
表4-10 预定义数值格式名称及其作用(续)
表4-11 预定义日期和时间格式名称及其作用
日期和时间
格式名称
作 用
General Date
如果 expression 同时包含了日期和时间,则显示它们。如果 expression 只包含日期或只包含时间,则缺少的信息不显示。日期的显示取决于用户的系统设置。
Long Date
使用用户的系统设置所指定的 Long Date 格式。
Medium Date
使用 dd-mmm-yy 格式(例如,03-Apr-93)。日期的显示取决于用户的系统设置。
Short Date
使用用户的系统设置所指定的 Short Date 格式。
Long Time
用用户系统的长时间格式显示时间,包括时、分、秒。
Medium Time
使用 hh:mm AM/PM 格式,显示小时、分钟和 AM 或 PM。
Short Time
使用 hh:mm 格式,显示小时和分钟。
表4-11 预定义日期和时间格式名称及其作用(续)
例4-1 格式函数举例:
Format(8315.4, "00000.00"),Format(8315.4, "#####.##")
08315.40 8315.4
?Format(8315.4, "##,##0.00"),Format(315.4,"$##0.00")
8,315.40 $315.40

Format(8315.4, "Fixed"),Format(8315.4, "Standard")
8315.40 8,315.40

Format(8315.4, "Percent"),Format(315.4,"Scientific")
831540.00% 3.15E+02

Format(Now, "m/d/yy"),Format(Now, "dddd, mmmm dd, yyyy")
11-11-04 Thursday, November 11, 2004

Format(Now, "d-mmmm h:mm")
11-November 21:50

Format(Now, "ddddd ttttt")
2004-11-11 21:51:16
6、其他函数
除了上面介绍的数学函数、转换函数、字符串函数、日期时间函数和格式输出函数以外,Visual Basic中还有测试函数(见表4-12)、与文件相关的函数(见4.6节)及其他函数(见表4-13)。
表4-12 测试函数
函数名及调用格式
功 能
返回值类型
EOF(filenumber)
测试文件是否结束
Boolean
IsArray(varname)
测试变量是否为数组
Boolean
IsDate(expression)
测试表达式expression是否可以转换成日期
Boolean
表4-12-1 测试函数(续)
函数名及调用格式
功 能
返回值类型
IsEmpty(expression)
测试变量expression是否已经初始化
Boolean
IsError(expression)
测试expression是否为一个错误值
Boolean
IsMissing(argname)
测试可选的 Variant参数是否已经传递给过程。
Boolean
IsNull(expression)
测试表达式是否不包含任何有效数据 (Null)
Boolean
IsNumeric(expression)
测试表达式的运算结果是否为数
Boolean
IsObject(identifier)
测试标识符是否表示对象变量
Boolean
VarType(varname)
返回表示变量或表达式的子类型的整型数据
Integer
注:
VarType(varname)函数的参数varname是一个 Variant,包含用户定义类型变量之外的任何变量。该函数的返回值为一整数,表示变量或表达式的子类型,其具体含义见表4-13。
表4-13中给出的常数是由 Visual Basic 为应用程序指定的。这些名称可以在程序代码中到处使用,以代替实际值。
VarType 函数自身从不对 vbArray 返回值。VarType 总是要加上一些其他值来指出一个具体类型的数组。常数 vbVariant 只与 vbArray 一起返回,以表明 VarType 函数的参数是一个 Variant 类型的数组。例如,对一个整数数组的返回值是 vbInteger + vbArray,或 8194。如果一个对象有默认属性,则 VarType (object) 返回对象默认属性的类型。
表4-13 VarType(varname)函数的返回值及其描述
常数

描 述
常数

描 述
vbEmpty
0
Empty(未初始化)
vbObject
9
对象
vbNull
1
Null(无有效数据)
vbError
10
错误值
vbInteger
2
整数
vbBoolean
11
布尔值
vbLong
3
长整数
vbVariant
12
Variant(只与变体中的数组一起使用)
vbSingle
4
单精度浮点数
vbDataObject
13
数据访问对象
vbDouble
5
双精度浮点数
vbDecimal
14
十进制值
vbCurrency
6
货币值
VbByte
17
位值
vbDate
7
日期
vbUserDefinedType
36
包含用户定义类型的变量
vbString
8
字符串
vbArray
8192
数组
表4-14 其他函数
函数名及调用格式
功 能
返回值类型
IIf(expr,e1, e2)
当expr为True时, 返回值为e1, 否则,返回值为e2
由e1或e2决定
Choose(index,c1[,c2,...[,cn]])
根据index的值来决定最终返回的值
由c1,c2等决定
Switch(expr-1,value-1[,expr-2,
value-2_[,expr-n,value-n]])
计算一组表达式列表的值,然后返回与表达式列表中最先为 True 的表达式所相关的 Variant 数值或表达式

Array(list)
返回一个包含数组的 Variant

LBound(arrayname[,dimension])
返回一个指定数组的维数可用的最小下标
Long
UBound(arrayname[,dimension])
返回一个指定数组的维数可用的最大下标
Long
CurDir[(drive)]
返回当前的路径
String
InputBox$(c1[,c2…])
打开一个输入对话框,等待用户输入正文或按下按钮,并返回包含文本框中内容的字符串。
String
MsgBox(prompt)
在对话框中显示消息,等待用户单击按钮,并返回一个 Integer 告诉用户单击哪一个按钮。
Integer
QBColor(color)
返回表示所对应颜色值的 RGB 颜色码
Long
RGB(red, green, blue)
返回表示一个 RGB 颜色值
Long
说明:
Choose(index,c1[,c2,...[,cn]])函数的用法参见3.6.2节Select Case语句。
Switch(expr-1,value-1[,expr-2,value-2_[,expr-n,value-n]])的作用是首先计算一组表达式列表的值,然后返回与表达式列表中最先为 True 的表达式所相关的 Variant 数值或表达式。其中,expr必选参数,表示要加以计算的 Variant 表达式;value必选参数,表示如果相关的表达式为 True,则返回此部分的数值或表达式。
Switch 函数的参数列表由多对表达式和数值组成。表达式是由左至右加以计算的,而数值则会在第一个相关的表达式为 True 时返回。如果其中有部分不成对,则会产生一个运行时错误。如果 expr-1 为 True 则 Switch 返回 value-1,如果 expr-1 为 False,但 expr-2 为 True,则 Switch 返回 value-2,以此类推。
Switch 会返回一个 Null值,如果:
没有一个表达式为 True。
第一个为 True 的表达式,其相对应的值为 Null。
LBound 函数与 UBound 函数一起使用,用来确定一个数组的大小。其用法参见3.5节数组。
QBColor(color)函数中color 参数是一个界于 0 到 15 的整型数,其对应的颜色值见表4-15。
表4-15 QBColor(color)函数中color 参数的值及其含义

颜色

颜色

颜色

颜色
0
黑色
4
红色
8
灰色
12
亮红色
1
兰色
5
洋红色
9
亮兰色
13
亮洋红色
2
绿色
6
黄色
10
亮绿色
14
亮黄色
3
青色
7
白色
11
亮青色
15
亮白色
color 参数代表使用于早期版本的 Basic(诸如 Microsoft Visual Basic for MS-DOS 以及 Basic Compiler)的颜色值。始于最低有效字节,返回值指定了红、绿、蓝三原色的值,用于设置成 VBA中RGB 系统的对应颜色。
RGB(red, green, blue)函数中的三个参数表示三原色,其取值范围都在0~255之间。表4-16是一些常见的由三原色合成的标准颜色。
表4-16 常见的标准颜色
颜色
红色值
绿色值
兰色值
颜色
红色值
绿色值
兰色值
黑色
0
0
0
红色
255
0
0
兰色
0
0
255
洋红色
255
0
255
绿色
0
255
0
黄色
255
255
0
青色
0
255
255
白色
255
255
255
4.2 过程概述 
在编制程序时,经常使用系统提供的事件过程和内部函数。其实,有时仅依赖系统提供的事件过程和内部函数,往往是不够的。在Visual Basic中,还允许用户自己定义以Sub关键字开始的子程序、以Function关键字开始的函数过程、以Property关键字开始的属性过程和以Event关键字开始的事件过程四类过程。
在程序设计中,对于一个复杂的应用问题,往往要把它逐层细分成一个个简单问题去解决,这是一个被称为 “自顶向下”、“逐步求精” 的过程。对每一个简单问题,都通过编制一段程序来实现,这段程序称为“过程”,也叫“子程序”。Visual Basic中的过程可以看做是编写程序的功能模块。Visual Basic应用程序就是由过程组成的。
当多个不同的事件过程需要使用一段相同的程序代码时,就可以把这一段代码独立出来,作为一个过程。这样的过程称为“通用过程”(general procedure),它可以单独建立,供事件过程或其他通用过程调用。使用通用过程编程,可以减少代码重复,也使应用程序的维护变得更加容易。Visual Basic中的通用过程分为两类,即子程序过程和函数过程。其中前者通常叫做Sub过程,后者通常叫做Function过程。
使用过程进行程序设计,主要有如下一些优点:
有利于程序的设计、调试及维护。这是因为将一个十分复杂的任务分解成多个较小的模块,每个模块的功能将变得比较简单、单一,这样,每个小模块无论从设计、调试、维护及改进等诸多方面,都将变得更为可行。
可以实现代码段的重复使用,简化程序设计,节省系统资源,提高系统效率。因为采用过程程序设计以后,相同的程序段只需编写一次,当用户需要调用该过程时,就象使用Visual Basic内部函数一样,在需要调用此过程的地方直接调用即可。
有利于组织多人合作编写大型应用程序,同时还可以提高编程效率。这是由于在一个程序中的过程,往往不必修改或只需稍作改动,便可以成为另一个程序的构件,从而大大提高了编程效率。
4.2.1 Sub过程的建立和调用
将程序分割成较小的逻辑部件(过程)就可以简化程序设计任务,它们可以变成增强和扩展 Visual Basic 的构件。
1. Sub过程的分类
在 Visual Basic 中,Sub过程可分为通用过程和事件过程两大类。其特点是执行完后没有返回值,因此,Sub过程不能出现在表达式中,不具有数据类型。
通用过程是多个不同的事件都要调用的子过程,它可以定义在窗体模块、标准模块或类模块中。
事件过程是和一个具体的窗体或控件相联系的过程,只能放在窗体模块中定义。事件过程不能由用户任意定义,只能由系统指定。当窗体或控件对一个事件的发生作出识别时,就会自动用事件的名称调用相应的事件过程,从而使相关事件得到及时响应。
不同模块中的过程(包括事件过程和通用过程)可以互相调用。
控件对象的事件过程名的格式是“控件名_事件名”,控件名就是该控件的Name属性。如果更改了控件的Name属性,则一定也要更改控件对象的事件过程名,以符合控件的新名字。否则,Visual Basic 无法使控件和过程相符。过程名与控件名不符时,过程就成为通用过程。例如,对一个名称为“开始”的命令按钮,用鼠标单击之后,会调用的控件事件过程是:开始_Click()。
窗体对象的事件过程名的格式是“Form_事件名”(注意,是Form而不是窗体名,这点与控件对象的事件过程名不同)。例如,如果希望在单击窗体之后,窗体会调用事件过程,则窗体对象的事件过程名要写成 Form_Click。如果使用的是多文档界面窗体(MDIForm),则窗体对象的事件过程名的格式是“MDIForm_事件名”。如多文档界面窗体的装入事件过程是 MDIForm_Load。
所有的事件过程使用相同的语法。
控件事件过程的语法:
Private Sub controlname_eventname (参数表列)
statements
End Sub
窗体事件过程的语法:
Private Sub Form_eventname (参数表列)
Statements
End Sub
通用过程如何被事件过程调用,见图4-1。
图4-1 事件过程调用通用过程示意图
注意:虽然可以自己编写事件过程,但使用 Visual Basic 提供的代码过程会更方便,这个过程自动将正确的过程名包括进来。从“对象框”中选择一个对象,从“过程框”中选择一个过程,就可在“代码编辑器” 窗口选择一个模板。
2. Sub过程的建立和调用
(1) Sub过程的定义
Sub过程是在响应事件时执行的代码块。定义Sub过程的语法格式是:
[Static] [Private|Public| Friend] Sub 过程名[(参数表列)]
语句块
[Exit Sub]
[语句块]
End Sub
说明:
① Sub过程以Sub开头,以End Sub结束,夹在Sub和End Sub之间的语句块,称为“过程体”或“子程序体”,它决定着子程序的功能。
② Static、Private、Public、Friend的作用:
Static指定过程中的局部变量在内存中的默认存储方式。如果使用了Static,则过程中的局部变量就是Static型的,即在每次调用过程时,局部变量的值保持不变;如果省Static,则局部变量就默认为“自动”的,即在每次调用过程时,局部变量被初始化为0或空字符串,而在调用子过程之后,局部变量的值全部消失。Static 属性对在 Sub 外声明的变量不会产生影响,即使过程中也使用了这些变量。通常Static关键字和递归的子过程不在一起使用。
Private表示Sub过程是私有过程(局部过程),只能被本模块中的其他过程访问,不能被其他模块中的过程访问。
Public表示Sub过程是公有过程,可以在程序的任何地方调用它。各窗体通用的过程通常在标准模块中用Public定义,在窗体层定义的通用过程一般在本窗体模块中使用,也可以在其他窗体模块中使用。如果在包含 Option Private 的模块中使用,则这个过程在该工程外是不可使用的。
Friend只能在类模块中使用,表示该Sub过程在整个工程中都是可见的,但对对象实例的控制者是不可见的。Private、Public、 Friend可任选其一,当都未选时,则Sub过程默认是公有的子过程(即默认为Public)。
③ 过程名的命名规则与变量名相同,它是一个长度不超过255个字符的标识符。
④ 参数表列是可选的,有参数表列的过程称为有参过程,否则,称为无参过程。参数表列中的参数是形式参数(简称形参),它可以是变量或数组,用来接收该过程被调用时传来的实际参数(简称实参),当有多个形参时,各个形参之间用逗号隔开。形参指明了调用时传送给过程的参数的个数和类型,每个形参的格式为:
[Option] [ByVal | ByRef] [ParamArray] 变量名[( )] [As数据类型] [默认值]
Option 是可选项,表示参数不是必需的关键字。如果使用了该选项,则形参中的后续参数都必须是可选的,而且必须都使用Optional关键字声明。如果使用了ParamArray,则任何参数都不能使用Optional。
ByVal | ByRef 只能选择一个。ByVal表示该参数按值传递。ByRef表示该参数按地址传递。ByRef是Visual Basic的默认选项。
ParamArray 可选的。只用于参数表列的最后一个参数,指明最后这个参数是一个Variant元素的Optional数组。使用ParamArray关键字可以提供任意数目的参数。ParamArray关键字不能与ByVal,ByRef,或Optional一起使用。
“变量名”是一个合法的Visual Basic变量名或数组名。如果是数组,则要在数组名后加上一对括号。
“数据类型”指的是变量的类型,可以是Byte、Boolean、Integer、Long、Currency、Single、Double、Decimal(目前尚不支持)、Date、String(只支持变长)、Object 或 Variant。如果没有选择参数 Optional,则可以指定用户定义类型,或对象类型。如果省略“As数据类型”,则默认为Variant。
默认值可选的。任何常数或常数表达式。只对 Optional 参数合法。如果类型为 Object,则显式的默认值只能是 Nothing。
在定义Sub过程时,“参数表列”中的参数不能是定长字符串变量或定长字符串数组。但在调用语句中可以用简单定长字符串变量作为“实际参数”,在调用Sub过程之前,Visual Basic把它转换为变长字符串变量。
⑤ Exit Sub 语句可以提前结束过程调用,使执行立即从一个Sub过程中退出。程序接着从调用该Sub过程的语句下一条语句执行。在Sub过程的任何位置都可以有Exit Sub语句。当然,该语句只有和条件选择语句配合才有实际意义。
⑥ 在Sub过程中使用的变量分为两类:一类是在过程内显式定义的,另一类则不是。在过程内显式定义的变量(使用Dim或等效方法)都是局部变量。对于使用了但又没有在过程中显式定义的变量,除非其在该过程外更高级别的位置有显示地定义,否则也是局部的。
⑦ Sub过程不能嵌套。也就是说,在Sub过程内,不能定义Sub过程或Function过程;不能用GoTo或Return语句进入或退出一个Sub过程,只能通过调用执行Sub过程,而且可以嵌套调用。
过程可以使用没有在过程内显式定义的变量,但只要有任何在模块级别定义的名称与之同名,就会产生名称冲突。如果过程中使用的未定义的变量与别的过程、常数或变量的名称相同,则认为过程使用的是模块级的名称。显式定义变量就可以避免这类冲突。可以使用 Option Explicit 语句来强制显式定义变量。
(2) Sub过程的建立
通用过程不属于任何一个事件过程,因此不能放在事件过程中。通用过程可以在标准模块和窗体模块中建立。如果在标准模块中建立通用过程,则有以下两种方法。
方法一:
① 单击“工程”菜单中的“添加模块”命令,打开“添加模块”对话框。选择“新建”选项卡,双击“模块”图标,打开模块代码窗口。
② 单击“工具”菜单中的“添加过程”命令,打开“添加过程”对话框。在“名称”框内输入要建立的过程的名字;在“类型”栏内选择“子程序”;在“范围”栏内选择过程的适用范围(即“公有的”或“私有的”)。如果选择“公有的”,则所建立的过程可用于本工程内的所有窗体模块;如果选择“私有的”,则所建立的过程只能用于本标准模块。如图4-2所示。
图4-2 “添加过程”对话框
③ 单击“确定”按钮,回到模块代码窗口。此时可以在Sub和End Sub之间键入程序代码。
方法二:
① 执行“工程”菜单中的“添加模块”命令。
② 打开模块代码窗口。
③ 键入过程的名字。例如,键入“Sub A1( )”,按回车键后显示:
Sub A1( )
End Sub
即可在Sub和End Sub之间键入程序代码。
在模块代码窗口中,通用过程出现在“对象”框的“通用”项目下,其名字可在“过程”框中找到。
如果在窗体模块中建立通用过程,则可双击窗体进入代码窗口,然后在“对象”框中选择“通用”,在“过程”框中选择“声明”,直接在窗口内键入“Sub A1()”,并按回车键,窗口内显示:
Sub A1( )
End Sub
此时即可键入代码。当然,最简单的办法就是手工在代码窗口中直接键入。
如果要建立事件过程,应先在对象框上选择对象,再到过程框上选择过程。例如:当在对象框上选择Command1,在过程框上选择了“Click”,则在代码编辑器窗口中将出现如下格式:
Private Sub Command1_Click( )

End Sub
此时,就可以在Sub和End Sub之间键入事件过程中所包含的程序代码了。
需要说明的是:在建立事件过程时,当选择了对象和相关的过程后,事件过程的名称将由Visual Basic自动给出;如果更改了控件的Name属性,则一定也要更改控件对象的事件过程名,以符合控件的新名字。否则,Visual Basic 无法使控件和过程相符,在控件识别事件时不执行任何操作。当过程名与控件名不符时,过程就成为通用过程。
(3) Sub过程的调用
事件过程的调用是当窗体或控件对一个事件的发生作出识别时自动进行的,而通用过程的调用是在程序中用调用语句实现的。使用过程调用语句可以将控制权从当前过程转移到另一个过程,执行完后再返回到当前过程的调用处再继续执行。
Sub过程的调用格式如下:
[Call] <过程名>[(实际参数表列)]
说明:
① 用Call语句调用一个过程时,如果过程本身没有参数,则“实际参数”和括号可以省略;否则应给出相应的实际参数,并把参数放在括号中。即:Call 过程名(实际参数表列)
② 如果省略关键字Call调用一个过程时,过程名可以作为一个语句使用,但必须省略实际参数表外的圆括号(除非没有参数)。 即:过程名 实际参数表列
③ 定义过程时的参数表列称为形参,调用过程时的参数表列称为实参,实参表列中列出了调用过程时要传递给过程的实际参数,各参数之间用逗号分隔。实参可以是变量、常量、数组或表达式。如果要将整个数组传递给一个过程,则可以使用数组名作实际参数并再数组名后加上空括号。实参与虚参在个数、类型和顺序上必须匹配。实参与虚参相匹配的过程称为参数传递,具体内容在4.3.2节讨论。
④ 通用过程之间、事件过程之间、通用过程和事件过程之间,都可以互相调用。甚至过程还可以自己调用自己(递归调用),有关具体内容在4.4节详细介绍。当过程名惟一时,可以直接通过过程名调用;如果两个或两个以上的标准模块中含有相同的过程名,则在调用时必须写上模块名加以限定,其格式为:模块名.过程名(参数表)
⑤ 当在一个模块中调用其他模块中的过程时,被调用的过程必须时“公有的”(Public)
⑥ 每个过程都有其作用域,有关过程作用域的介绍见4.5节。
下面两条语句是等价的,其作用都是调用有参过程ABC。
Call ABC(x1,x2,x3)
ABC x1,x2,x3
下面两条语句也是等价的,其作用都是调用无参过程XYZ。
Call XYZ
XYZ
例4-2 编写一个交换两个整型变量值的过程Swap,然后在窗体的Click事件过程中,随机产生两个小于1000的正整数,调用过程Swap完成对其值的交换,并输出交换前后的结果。
程序代码如下:
Sub Swap(x As Integer, y As Integer)
Dim t as Integer
T=y:y=x:x=t
End Sub
Sub Form_Click( )
Dim a As Integer, b As Integer
a=Int(Rnd*1000)
b=Int(Rnd*1000)
Print "交换前:"
Print "a=";a,"b=";b
Swap a,b '或 Call Swap(a,b)
Print "交换后:"
Print "a=";a,"b=";b
End Sub
4.2.2 Function过程的建立和调用 
Visual Basic的函数分为标准函数和用户自定义函数。在前面的章节,已介绍了部分常用的标准函数,本小节介绍用户自定义函数——以Function关键字开始的函数过程。
无论是Sub过程还是Function过程,它们都是能完成一定功能的程序段。所不同的是,Sub过程不存在类型问题,调用完毕后,不返回结果;而Function过程具有一定的数据类型,调用后能够返回一个相应数据类型的值,即函数值。因此,当仅仅需要完成某种例行操作而无需返回结果时,一般使用Sub过程;如果在完成相关操作后还需返回一个最终结果,则一般使用Function过程。当然,如果需要返回的值不止一个时,就需要借助其他手段来实现,这方面的内容在4.3.2节介绍。
(1) Function过程的定义
定义Function过程的语法格式是:
[Static] [Private|Public| Friend] Function 函数名[(参数表列)] [As 类型]
语句块1
[函数名=表达式]
[Exit Function]
[语句块2]
[函数名=表达式]
End Function
说明:
定义函数过程和定义子过程基本上是一样的,格式中相同的关键字的含义和使用方法,以及参数表列中参数的格式完全一样。与Sub过程相比,其不同点在于:
① Function过程以Function开头,以End Function结束,两者之间是描述过程操作的语句块,即“函数体”。
② 在过程体中如果执行了Exit Function语句,则将退出函数过程,返回函数值。当然,Exit Function语句只有和选择语句配合才有实际意义。
③ “As类型”是可选的,用来指定由Function过程返回的值的数据类型,可以是Integer,Long,Single,Double,Currency或String;如果省略,则为Variant。不允许任何类型的数组作为返回值,但包含数组的变体型变量可以作为返回值。
④ 要从函数返回一个值,只需将该值赋给“函数名”即可。并且可以在函数体的任意位置上都可出现这种赋值。如果在函数体中对函数名没有赋值,则该函数过程将返回一个默认值: 数值函数过程返回0值,字符串函数过程返回空字符串,变体型函数返回Empty。如果在返回对象引用的函数中没有将对象引用赋给函数名(通过SET语句),则函数返回值为Nothing。例如:
Function Cub( a As Double) As Double
Dim res
res = a*a*a*a
Cub = result
End Function
该例定义了一个Cub函数,它有一个参数,其返回值为Double。函数的返回值等于参数的四次方。
⑤ 同sub过程一样,函数的定义也不能嵌套。因此,不能在事件过程中定义通用过程(包括Sub过程和Function过程),只能在事件过程内调用通用过程。
(2)Function过程的建立
Function过程的建立与通用子过程的建立类似。如果在窗体模块中建立函数过程,可以直接在代码编辑器窗口中完成。在打开代码编辑器窗口后,可在对象框上选择“通用”,然后按定义函数过程的语法格式进行。例如,当在编辑器窗口中输入:Function fun1(x1%,x2%)并按回车键后,代码编辑器窗口中将出现如下格式:
Function fun1(x1%,x2%)

End Function
这里,fun1是函数名,x1%,x2%是形式参数。此时,可以在Function和End Function之间输入该函数所包含的程序代码了。
如果是在标准模块中建立函数过程,则用类似建立子过程的方法,在打开图4-2所示的对话框后,在类型单选按钮中选择“函数”即可,其他和建立子过程完全一样。
(3)Function过程的调用
调用Function过程与调用Visual Basic内部函数的方法一样,没有什么区别,只不过内部函数由语言系统提供的,而Function过程由用户自己定义的。调用结束后都会返回一个函数值。
调用函数过程(包括VISUAL BASIC内部函数和用户自定义函数)和调用通用过程基本一样,可以用调用过程语句,也可以将函数直接作为一条语句来用,但这样调用的结果是:函数的返回值将被放弃。
如果调用函数过程需要返回一个函数值,则不能使用语句调用方式,而必须使用表达式调用方式,即 变量名=函数过程名(参数表列) 。这是调用函数过程和调用通用子过程的一个重要区别,此时,在定义的函数过程中必须有给函数名赋值的语句。
当然,调用有参函数过程,仍然需要注意实参和形参的相互匹配问题。具体细节将在4.3.2节中介绍。
例如:下面的三条语句都调用同一个有参函数过程,但只有第三条语句的函数值能够保留,其他两条调用语句的函数值都将被放弃。
Call Fun(x1,x2,x3)
Fun x1,x2,x3
X= Fun(x1,x2,x3)
下面的三条语句都调用同一个无参函数过程,但也只有第三条语句的函数值能够保留,其他两条调用语句的函数值也将被放弃。
Call Fun
Fun
X= Fun
例4-3 编程计算表达式 (m≥n>0)的值。
要求:m,n的值用输入对话框实现,求阶乘用函数过程实现。
程序代码如下:
Function Fact(n As Integer) As Long
Dim i As Integer
Fact=1
For i=1 To n
Fact=Fact*i
Next i
End Function
Private Sub Form_Click( )
Dim m As Integer, n As Integer
Do
m = Val(InputBox("请输入m的值:"))
n = Val(InputBox("请输入n的值:"))
Loop While mPrint fact(m)/fact(n)/fact(m-n)
End Sub
4.3 过程之间参数的传递
每当调用一个过程时,必须把实际参数传送给过程中的形式参数,完成形式参数与实际参数的结合,然后用实际参数执行调用的过程。
Visual Basic中,过程之间的数据传递方式有如下两种:
通过实参与形参相结合。
通过窗体/模块级变量或全局变量在过程间共享数据。
本节重点讨论第一种方式,第二种方式参见3.3节、4.5.2节。
4.3.1 形参与实参 
形参又称虚参或形式参数,是在定义过程时出现在Sub或Function语句圆括号中的参数。形参可以是除定长字符串数据外的各种数据类型的变量,也可以是后跟圆括号的数组名。它不能是常量或表达式。在过程没有被调用以前,形参仅仅是一个无实际值的记号,其作用仅限于说明在过程体中需要用到一个样的数据类型,以及需要对这些数据进行怎样的处理。
实参又称实际参数,是在调用过程时出现在过程名后的参数。实参可以是常量、变量、表达式、数组名。但实参必须有具体值,其作用是向被调过程的虚参传递数据。
当形参或实参的个数多于一个时,各个参数之间用逗号隔开。
4.3.2 参数传递(虚实结合)
参数传递又称虚实结合,它是指主调过程将实参的相关数据传送给被调过程中形参。事实上,在一个过程被调用之前,系统并没有为形参分配存储单元,直到调用时才分配。此时,形参的存储单元中的内容就是从相应的实参中传递得到的。在过程调用结束之后,形参所占用的存储单元将被系统收回,形参又恢复为无值状态。
虚实结合时,实参和虚参要做到:个数要相等;顺序要对应;类型要一致。
注意:在形式参数表中只能使用形如x$或x As String之类的变长字符串作为形参,不能用形如str As String*8之类的定长字符串作为形参;但定长字符串可以作为实际参数传送给过程。
在Visual Basic中,实参和形参之间可以通过两种方式传递数据信息,一种时按值传送,一种是按地址传送。究竟是按值传送还是按地址传送,关键取决于参数的传递形式。
1.按值传送
如果在调用过程时,实参是常量或表达式,或在定义通用过程时,如果在形参前面加了ByVal关键字,则参数将按值传送。另外,如果在定义通用过程时,在形参前面没有加关键字ByVal,而又要求实参变量按值传送,此时只需将实参变量用一对圆括号扩起来即可。
按值传送时,Visual Basic为形参分配内存单元,并将实参(可以是常量、变量或表达式)的值复制一份传给对应的虚参,由于所有的变化都是在变量的副本上进行的,所以不会改变实参的值。
例4-4定义一个按值传送的子过程,并调用此子过程观察实参的变化情况。
首先建立一个窗体,在窗体上添加一个命令按钮,在窗体的通用部分建立子过程AZCS(按值传送),然后在命令按钮的单击事件过程中调用子过程AZCS,程序代码如下:
Sub AZCS (ByVal i1 As Integer, j1 As Integer)
i1 = i1 * 10
j1 = 20+ j1
Print "i1="; i1, "j1="; j1
End Sub
Private Sub Command1_Click()
Dim i%, j%
i = 1000
j = 2000
Call AZCS (i, (j))
Print "i="; i, "j="; j
End Sub
程序运行后单击命令按钮,输出如下:
i1=10000 j1=2020
i=1000 j=2000

可见,用按值传送调用子过程,不会改变实参的值。
注意:上面调用子程序程序AZCS时,实参j外面一对圆括号的作用。如果去掉这对圆括号,结果又是多少呢?
2.按地址传送
在定义通用过程时,如果在形参前面加了关键字ByRef(默认方式)或什么关键字都没有加,则参数将按地址传送。调用过程时,系统将实参的地址传送给对应的形参,即实参和形参共享同一个存储单元。如果在过程中修改了形参的值,实参的值也将随之发生变化,所以,当过程执行结束调用程序单位时,实参的值就是被调用程序单位中相应形参的最终值。可见,在这种虚实结合方式中,实参的值在调用前后将会发生变化。
在按地址传送参数时,实参只能是变量,不能是常量或表达式,否则,将不是按地址传送,而是按值传送。
如果不希望在调用过程时改变实参变量的值,则应使用传值方式。但是一般而言,传地址比传值更能节省内存和提高效率,因为传地址(引用)时系统不必为保存它的值而分配内存空间,只需记住实参的地址即可。这种效率上的差异,在整型数的传送上表现的不太明显,但在字符串(尤其是长字符串)的传送上,效率差异是很大的。
例4-5 将上例4-4中子过程AZCS的形参表改为
(ByRef i1 As Integer, ByRef j1 As Integer)
或 ( i1 As Integer,j1 As Integer),
在命令按钮的单击事件过程中,用Call(i,j)调用子过程AZCS,观察运行结果及实参的变化情况如下:
i1=10000 j1=2020
i=10000 j=2020
分析此运行结果可知,采用按值传送的方式传递参数,会改变实参的值。
这里再强调一下:当把形参定义为传址方式时,与其对应的实参仅当为变量时,实参才会随形参的改变而改变。如果实参是常量、表达式或将实参变量用圆括号括起来,则调用过程时将按值传送,这时,形参的改变将不影响实参的值。
综上所述,调用过程时,参数传递既可以按值传送,也可以按地址传送,两者各有优缺点。采用按值传送时,实参和形参因使用不同的存储单元,形参的变化不会影响实参的值。但当传送的是字符串或数组时,将会占用大量内存,效率不及按地址传送高。采用按地址传送时,虽然效率高,但由于实参和形参公用相同的存储单元,形参的变化导致实参的改变,有可能对调用程序带来副作用。故在定义过程时,究竟采用何种方法传递参数,要根据具体情况而定。
用户定义的类型(记录)和控件只能采用按地址传送。
Function过程可以通过过程名返回一个函数值,Sub过程不能通过过程名返回值。但它们都可以通过参数返回多个值(采用按地址传送时)。利用这一特性,可以用子程序实现函数过程的功能。
3.数组参数
在一个通用过程中使用数组作为参数时,除遵循参数的基本原则外,还须注意如下几点:
① 数组作形参时,一般不声明大小,但括号不能省略,以便与普通变量混淆。
② 数组作形参时,只能采用按地址传送方式,即在定义数组时,前面不能加关键字ByVal。
③ 在过程体内,形参数组的下标变化范围可以使用LBound函数或UBound函数来确定。例如,要将一个二维数组中的元素输出,则通用的程序段如下:
Sub printarray(x( ))
Dim row%,col%
For row=LBound(x,1) to UBound(x,1)
For col=LBound(x,2) to UBound(x,2)
Print tab(6*col);x(row,col);
Next col
Print
Next row
End Sub
④ 调用通用过程时,如果形参是数组,则相应的实参必须是与形参同类型的数组。实参数组的格式为:实参数组名( )或实参数组名。
⑤ 整个数组必须以ByRef(按地址方式)传递,但数组中数组元素可用ByVal(按值)传递。当数组元素作为实参出现时,一定要写上下标。
⑥ 数组作参数时,对形参数组元素的改变,将最终改变实参数组中相应元素的值。
例4-6 随机产生30个小于1000的正整数,对其进行排序,并输出排序结果。
分析:对多个数的排序,必须借助于数组来解决。而对于给定范围内的若干整数,可以在循环内借助随机函数生成,生成的每一个随机数,及时保存在数组元素中。本题可以用三个过程和一个主调程序组成,三个过程分别是:产生随机数并保存在数组元素中(getarray)、输出数组元素(printarray)、对数组元素排序(sortarray),主调程序可设计为Form_Click事件,在该事件中完成对上述三个过程的调用。
程序代码如下:
Sub getarray(x() As Integer)
Dim i%
For i = LBound(x) To UBound(x)
x(i) = Int(Rnd * 1000)
Next i
End Sub
Sub printarray(x() As Integer)
Dim i%
For i = LBound(x) To UBound(x)
Print x(i); " ";
Next i
Print
End Sub
Sub Swap(x As Integer, y As Integer)
Dim t As Integer
t = x: x = y: y = t
End Sub
Sub sortarray(x() As Integer)
Dim i%, j%, k%, t%
For i = LBound(x) To UBound(x) - 1
k = i
For j = i + 1 To UBound(x)
If x(k) > x(j) Then k = j
Next j
If k <> j Then Swap x(k), x(i)
Next i
End Sub

Private Sub Form_Click()
Const n = 30
Dim a(1 To n) As Integer
getarray a
Print "排序前的结果为:"
printarray a
sortarray a
Print "排序后的结果为:"
printarray a
End Sub
4.记录参数
记录是用户自定义的数据类型,记录也可以作通用过程(Sub过程和Function过程)的参数,其用法和简单变量作通用过程的参数相似。需要注意的是,当形参是记录型变量时,实参必须是与形参同类型的变量。
单个记录元素也可以作实参,此时应写成“记录名.元素名”的形式,也要注意实参、形参的类型匹配问题。
例4-7 定义一记录,记录元素包含编号、姓名、性别、生日、成绩(中文、数学、英语),然后完成对记录元素的赋值、引用,并注意记录作参数的用法。
首先建立一窗体,在窗体的声明部分定义记录数据类型Student和记录型变量stu1。代码如下:
Private Type Score_Rec ' 先定义记录类型
Chinese As Integer
Math As Integer
English As Integer
End Type
Private Type Student
Num As Long
Name As String * 20
Sex As Boolean
Birthday As Date
Score As Score_Rec ' 记录嵌套
End Type
Dim stu1 As Student ' 定义记录类型变量
在窗体的单击事件中输入如下代码:
Private Sub Form_Click()
stu1.Num = 200401 ' 给记录型变量stu1的各个元素(成员)赋值
stu1.Name = "刘 明"
stu1.Sex = 0
stu1.Birthday = #10/1/1985#
stu1.Score.Chinese = 90
stu1.Score.Math = 95
stu1.Score.English = 85
Call printout(stu1) ' 以数组变量为实参,调用子过程printout,
End Sub ' 实参和形参均为记录类型
在窗体中建立如下子过程:
Private Sub printout(s As Student) ' 输出s参数中各元素的值
Print
Print "编号:" & s.Num; Tab(25); "姓名:"& s.Name
Print
Print "性别:";
If s.Sex = False Then Print "女"; Else Print "男";
Print Tab(25); "生日:"; s.Birthday
Print
Print "中文:" & s.Score.Chinese; Tab(13); "数学:" & s.Score.Math;
Print Tab(25); "英语:" & s.Score.English
End Sub
运行时单击窗体,结果如图4-3所示:

图4-3 例4-7的运行结果
5.可选参数和可变参数
Visual Basic提供了十分灵活和安全的参数传送方式,允许使用可选参数和可变参数。在调用一个过程时,可以向过程传送可选的参数或者任意数量的参数。
(1) 可选参数
如果过程中形参的个数是固定的,在调用过程时,要求实参和形参的的个数要相等、类型要相容、次序要一致;否则,在调用过程时就会出错。
如果在定义过程时,在形参表中某一个参数使用了关键字Optional,则该关键字后的参数都是可选参数,且从第一个使用了关键字Optional参数起,其后的参数全部要用关键字Optional定义。也就是说,形参表中可选参数的数目不限,但是,必须放在参数表的最后,而且必须是Variant类型(即未指定类型)。调用时,允许实参的个数少于形参的个数,此时,形参使用默认值。
例如:
Private Sub Form_Click()
Call cxcs(100, 200, 300)
End Sub
Private Sub cxcs(x1 As Integer, x2 As Integer, Optional x3)
If IsMissing(x3) Then
m = x1 + x2
Else
m = x1 + x2 + x3
End If
If IsMissing(x3) Then
Print "向可选参数未传值,两数之和为:" & m
Else
Print "向可选参数已传值,三数之和为:" & m
End If
End Sub
运行结果为:
向可选参数已传值,三数之和为:600
如果将 Call cxcs(100, 200, 300)改为Call cxcs(100, 200),则运行结果如下:
向可选参数未传值,两数之和为:300
(2) 指定可选参数的默认值
定义过程时,当把某个形参使用关键字Optional定义为可选参数的同时,还可以为该可选参数设定默认值,格式为:
Optional <形参变量> As <类型>=值
在此情况下,如果未向可选参数传值,则按指定的默认值计算;否则,则按实际传的值计算。
注意:当为可选参数设置了默认值时,此时再用IsMissing函数测试可选参数,返回值均为False(即系统认为在此情况下,可选参数都被传了值),因此,再再用IsMissing函数测试可选参数的值,已没有实质性的意义。
还以上例为例,将程序代码修改为:
Private Sub Form_Click()
Call cxcs(100, 200, 300)
End Sub

Sub cxcs(x1 As Integer, x2 As Integer, Optional x3 As Integer = 1000)
m = x1 + x2 + x3
Print "可选参数的值为" & x3; ",三数之和为:" & m
End Sub

运行结果为:
可选参数的值为300,三数之和为:600
如果将 Call cxcs(100, 200, 300)改为Call cxcs(100, 200),则运行结果如下:
可选参数的值为1000,三数之和为:1300
(3) 可变参数
在定义过程时,如果在形参的前面使用了关键字ParamArray,则此过程可以接受任意多个参数。故在调用这样的过程时,实参的个数是不固定的,可以根据实际需要,可多可少。
可变参数过程通过ParamArray命令来定义,一般格式为:
这里的“数组名”是一个形式参数,只有名字和括号,没有上下界。由于省略了变量类型,“数组”的类型默认为Variant,因此可以把任何类型的实参传递给该过程。
在含有可变参数的过程体中,如果要处理传给Variant型数组中的数据时,则可以使用专门针对数组或对象“集合”循环语句——For Each...Next语句,它是一种特殊的循环语句。
Sub过程名(ParamArray数组名( ))
例4-8 设计一个通用过程,统计下面每组数的个数并计算其平均值。
第1组 34, 67, 89, 12, 45, 77, 25
第2组 346, 56.7, 456.2, 4352.785, 125.87
第3组 23.3, 56.45, 768.5, 4234, 23.8

在窗体的代码窗口中分别定义整型变量p,通用子过程JSPJZ以及窗体的单击事件过程,代码如下:
Dim p As Integer '用来统计当前是第几次调用子过程jspjz
Private Sub Form_Click()
p = 0
Call jspjz(34, 67, 89, 12, 45, 77, 25)
Call jspjz(346, 56.7, 456.2, 4352.785, 125.87)
Call jspjz(23.3, 56.45, 768.5, 4234, 23.8)
End Sub
Sub jspjz(ParamArray x())
Dim n ' 用于统计每一组中数据的个数
Dim sum As Single ' 用于存放累加和
Dim aver As Single ' 用于存放平均值
Dim item ' 用于表示某一组中的某个数
p = p + 1
Print "第" & p; "组";
For Each item In x
Print item; " ";
sum = sum + item
n = n + 1
Next item
aver = sum / n
Print
Print "共有" & n; "个数,平均值为" & aver
Print
Print
End Sub
思考:在Form_Click()事件过程中,去掉p = 0,运行时单击窗体,会出现什么问题?
6.对象参数
在VISUAL BASIC中,通用过程一般用变量作参数,但同时,还允许用窗体或控件作参数,即允许用对象作参数。在某些情况下,使用对象参数可以简化代码冗余,提高程序效率。有关窗体和控件的问题,将在下一章作详细介绍。这里仅介绍它们作对象参数的用法。
从严格意义上说,用对象作为参数和用其他数据类型作为参数一样,没有什么区别。惟一的差别就是形参表中形参的类型是Form或Control。另外,在调用含有对象的过程时,只能采用按地址传送方式,而不能采用按值传送方式,因此,在定义过程时,不能在对象参数的前面加关键字ByVal。
(1) 窗体参数
定义通用过程时,如果用窗体作为参数时,则“形参表”中形参的类型必须为Form。且在调用时,只能通过传地址方式传送。
例如:如果要建立若干属性相同、惟有名字不同的多个窗体(MDI,在第7章介绍),则可以编写一个以窗体作为参数的通用过程:
Sub FormSet(FromNum As Form)
FromNum.X1 = XX1 ' 这里用X1,X2,…,Xn表示窗体的属性
FromNum.X2 = XX2 ' 这里用XX1,XX2,…,XXn表示窗体相应属性的值
……
FromNum.Xn = XXn
End Sub
调用时,可以用窗体作实参。例如:
FormSet Form1
此时,系统将按通用过程FormSet中提供的属性值来设置Form1窗体。
由于在默认情况下,第一个建立的窗体(在此为Form1)是启动窗体,所以,如果要调用通用过程FormSet来建立与窗体参数FromNum属性相同、名字不同的多个窗体,则首先必须利用“工程”菜单中的“添加窗体”命令建立多个窗体,即窗体1,窗体2,……,窗体n,然后创建窗体1(Form1)的Form_Load事件过程,在此事件过程中根据需要调用n次通用过程FormSet即可。即:
Private Sub Form_Load( ) 'Form1的Form_Load( )事件过程
FormSet Form1
FormSet Form2
……
FormSet Formn
End Sub
(2) 控件参数
定义通用过程时,用控件作为参数与用窗体作为参数相似,只不过“形参表”中形参的类型是Control。在调用时,也只能通过传地址方式传送。
由于不同控件的属性各不相同,所以控件参数的使用要比窗体参数的使用要复杂。具体体现在用指定的控件调用通用过程时,如果通用过程中的某个属性不属于此控件时,将会发生错误。为此,Visual Basic专门提供了一个TypeOf语句,用来在过程中限定控件参数的类型,避免此类错误的发生。其格式为:
{If | ElseIf} TypeOf 控件名称 Is 控件类型
TypeOf语句放在通用过程中。这里“控件名称”就是控件参数(形参)的名字,即“As Control”之前的参数名。“控件类型”就是各种不同类型控件的关键字。这些关键字有:复选框(CheckBox),单选按钮(OptionButton),组合框(ComboBox),列表框(ListBox),文本框(TextBox),目录列表框(Dir ListBox),驱动器列表框(DriveListBox),文件列表框(FileListBox),框架(Frame),命令按钮(CommandButton),标签(Lable),菜单(Menu),图片框(PictureBox),时钟(Timer),水平滚动条(HScrollBar),垂直滚动条(VScrollBar)。
4.4 过程的嵌套调用和递归调用
4.4.1 过程的嵌套调用 
在Visual Basic中,选择结构、循环结构允许嵌套定义,过程不能嵌套定义(即过程内部不能包含另一过程),所有的过程是平行和独立的。尽管过程不能嵌套定义,但允许过程嵌套调用,即允许一个过程调用另一个过程,而另一个过程又调用其他过程。嵌套调用的层数受堆栈大小的限制。
前面数组参数中的例4-6就属于过程的嵌套调用,其嵌套调用过程如图4-4所示。
图4-4 嵌套调用过程示意图
4.4.2 过程的递归调用 
递归就是用自身的结构来描述自身,即在一个过程的过程体中直接或间接的调用过程本身。递归分为两种类型,一种是直接递归,即在过程中调用过程本身;一种是间接递归,即间接地调用一个过程。例如,第一个过程调用了第二个过程,而第二个过程又回过头来调用第一个过程。
能够使用递归的方法进行求解的问题,必须满足以下两个基本条件:第一,存在递归的终止条件和终止值;第二,能用递归形式表示,且递归向终止条件发展。例如,求n!问题就满足以上两条件。递归调用在完成阶乘运算、级数运算、幂指数运算等方面特别有效。
Visual Basic的Sub过程和Function过程均具有具有递归调用功能,即允许在其过程体中调用自己,将这样的子程序或函数过程称为递归子程序或递归函数。在执行递归操作时,Visual Basic把递归过程中的信息保存在堆栈中,而堆栈的容量是有限的,如果因递归调用的嵌套层次太多而超过规定的递归次数,则将产生“堆栈溢出”错误,使程序运行出错。
递归求解分为两个阶段。第一个阶段是“递推”,第二个阶段是“回推” 。
注意:虽然递归算法设计简单,程序简练,但对同一问题,使用递归算法所消耗的时间和内存空间要比非递归算法要多得多,因此,能够使用其他方法解决的问题最好不要用递归解决。
通常 Static关键字和递归的Sub 过程以及递归Function过程不在一起使用。
例4-9 用递归过程计算n!(n≥0)
由数学知识可知:负数无阶乘,0的阶乘等于1,正数n的阶乘为:
n!=n(n-1)!,(n-1)!=(n-1)(n-2)!,……,1!=1*0!,0!=1,即:
n!=
1 (n=0)
n*(n-1) (n>10)
程序代码如下:
Private Sub Form_Click()
Dim n As Integer
Do
n = Val(InputBox("请输入一个正整数"))
Loop While (n < 0)
Print n; "!=" & fact(n)
End Sub
Function f(m%) As Long
If m = 0 Then
f = 1
Else
f = m * f(m - 1)
End If
End Function
若运行后输入得正整数为5,则递归调用过程如图4-5所示。
图4-5 5!递归调用过程示意图
4.5 过程的作用域 
4.5.1 程序模块概述
一个Visual Basic的应用程序也称为一个工程。一个工程一般由三类模块组成,即窗体模块(Form)、标准模块 (Module)和类模块(Class)。它们之间的关系如图4-6所示。模块(Module)是相对独立的程序单元。每个工程可以包含多个上述模块,所有模块共同属于一个工程。但每个模块又相对独立,用一个单独的文件保存,例如:.bas文件(标准模块)、.frm文件(窗体模块)、 .cls文件(类模块)、 .vbp文件(工程)、.vbg文件(工程组)。但在装入时,只需装入.vbp文件(单工程)或.vbg文件(多工程组),其他与该工程有关的文件将在工程管理器窗口中显示出来。
图4-6 Visual Basic应用程序结构
1.窗体模块 
窗体模块(.frm文件)是大多数应用程序的基础。窗体模块由声明部分、通用过程(Sub过程和Function过程)和事件过程等三部分组成。在声明部分,可以包含窗体级的常量、变量、类型定义以及通用过程的声明,被声明的变量的作用域是整个窗体模块(包括该模块内的每个过程)。在窗体模块代码中,声明部分一般放在最前面,通用过程和事件过程的位置没有限制。另外,窗体模块文件还包含窗体本身的数据属性、方法和事件过程;窗体还包含控件,每个控件都有自己的属性、方法和事件过程集。简单的应用可以只包含一个窗体模块。
2.标准模块 
标准模块(.bas文件)是由一些与特定窗体或控件无关的代码组成的另一种模块。标准模块由全局变量声明、模块层声明和通用过程(Sub过程和Function过程)等几部分组成。如果一个过程可能用来响应几个不同对象中的事件,则应将此过程放在标准模块中,而不必在每一个对象的事件过程中重复相同的代码。标准模块通过“工程”菜单中的“添加模块”命令来建立或打开。
全局变量声明放在标准模块的首部,用Public或Global声明,且变量名必须惟一。模块层声明包括在标准模块中使用的变量和常量,模块层变量用Dim或Private声明。当需要声明的全局变量或常量较多时,可以把全局声明放在一个不含任何过程的单独模块中,此模块将在所有基本指令开始之前处理。
在大型应用程序中,主要操作在标准模块中执行,程序和用户之间的通信在窗体模块中实现。但如果应用程序只有一个窗体,所有操作都可通过窗体实现,在此情况下,标准模块就不是必需的。
多个标准模块可以同时存在于一个工程文件中,这些标准模块可以是新建的,也可以是从原有模块中加入的。当然,这些标准模块的过程名以及每一个标准模块内的过程也不能重名。
通常情况下,Visual Basic从启动窗体开始执行指令。在此之前,不会执行标准模块中的通用过程,故只能在窗体或控件事件过程中调用。
另外,在标准模块中,还可以包含一个特殊过程Sub Main。这是由于在一个含有多个窗体或多个工程的应用程序中,有时需要在显示多个窗体之前对有关条件进行初始化,这就需要在启动时执行一个特定过程,这过程便是Sub Main过程,它类似于C语言中的Main函数。和在标准模块中建立其他过程的方法一样,建立Sub Main过程的方法是:执行“工程”菜单中的“添加模块”命令,打开标准模块窗口并键入:Sub Main,按回车后,在该过程的开头和结束语句之间输入程序代码即可。Sub Main过程在标准模块中必须是惟一的。
在一般情况下,由于应用程序从设计时的第一个窗体开始执行,所以,经常将需要最先执行的程序代码放在Form_Load事件过程中。当需要从其他窗体开始执行应用程序时,则可以通过“工程”菜单中的“工程属性”命令(“通用”选项卡)指定启动窗体。当Sub Main过程出现在标准模块中时,可以先执行Sub Main过程,但不是必须。
与C语言中Main( )不同的是,Sub Main过程并不能被Visual Basic系统自动识别,当希望将它指定为启动过程时,方法与指定启动窗体一样。只需将“启动对象”选为“Sub Main”即可。如图4-7所示。
图4-7 指定Sub Main为启动过程
当把Sub Main过程指定为启动过程时,则可以在运行时最先自动执行(优先于窗体模块),因此常用来设置初始化条件或指定其他过程的执行顺序。
3.类模块  
在 Visual Basic 中类模块(文件扩展名为 .CLS)是面向对象编程的基础。它与窗体模块类似,只是没有可见的用户界面。可在类模块中编写代码建立新对象。这些新对象可以包含自定义的属性和方法。这些对象可以被应用程序内的过程调用。实际上,窗体正是这样一种类模块,在其上可安放控件、可显示窗体窗口。标准模块只包含代码,而类模块既包含代码又包含数据,可视为没有物理表示的控件。
在类模块中,对模块级的变量不能声明为 Static。Static 数据只能在过程中使用。
注意:除上述文件外,一个工程还包括如下几个附属文件,它们在资源管理窗口中是看不到的。
(1) 资源文件(.res)。 它包含不必重新编辑代码就可以改变的位图、字符串和其他数据。
(2) 窗体的二进制数据文件(.frx) :若窗体上的控件含有二进制属性,当保存窗体文件时,就会自动产生与窗体文件同名的.frx文件。
ActiveX控件的文件(.ocx):它是一段设计好的可以重复利用的程序代码和数据,可以添加到工具箱,并可象其他控件一样在窗体中使用。
将过程可以被调用的范围称为过程的作用域。Visual Basic过程的作用域有两种:
4.5.2 过程的作用域 
这类过程可以在窗体模块或标准模块中定义。定义时必须在用户自定义的子过程或函数过程前面加上Private关键字。该子过程或函数过程只能被定义它的模块中的过程调用。因此,这类过程又被称为局部过程或私有过程,它不能被本工程中的其他模块调用。
1.窗体/模块级
这类过程也可以在窗体模块或标准模块中定义。定义时可以在用户自定义的子过程或函数过程前面加上Public关键字或省略。该子过程或函数过程既可以被定义它的模块中的过程调用,也可以被本工程中的其他模块调用。因此,这类过程又被称为全局过程或公有过程。在Visual Basic中,全局过程最好在标准模块中定义。
2.全局级
当本工程中的其他模块调用标准模块中的全局过程时,如果过程名惟一,可直接调用,否则,如果在各标准模块中有相同的过程名,则调用非本模块中的过程,则必须在过程名前面加上模块名加以限制。即:
Call 模块名.过程名(实参表)
同样,当本工程中的其他模块调用窗体模块中的全局过程时,要在过程名前面加上包含此过程的窗体的名称加以限制。即:
Call 窗体名.过程名(实参表)
4.6 文件的操作
在程序中要处理的数据信息,除了从普通终端(键盘、显示器)输入、输出外,还经常需要从文件中读入信息或将信息写入文件保存。通常计算机所处理的数据信息都是以文件的形式存放在外部物理介质(如磁盘、光盘等)上的,这样,数据不仅可以长期保存,而且还可以供其他程序使用,实现数据共享,减少数据冗余。
文件是指存储在外部物理介质上数据的集合。计算机操作系统以文件为单位来管理数据,,文件在存取时都是按名存取的,因此,每个文件都必须有一个区别于其他文件的名字,即文件名(又叫文件说明)。
完整的文件说明由设备名和文件引用名构成。对于磁盘文件来说,还可以含有路径。设备名通常为A:,B:,C:等,分别代表A,B,C等驱动器。文件引用名由两部分组成,即文件基本名和扩展名,它们之间由小数点(.)隔开。一般文件名表示文件的内容提要,扩展名表示文件的类型。如Visual Basic窗体文件的扩展名为.frm,工程文件的扩展名为.vbp,标准模块文件的扩展名为.bas,可执行文件的扩展名为.exe,等等。
文件基本名和扩展名可用下列字符表示: 英文字母、数字及某些特殊字符。在Windows环境中,文件说明不区分大小写字母。
例如:
C:\MyProject\ball.vbp
为了迅速有效地存取数据,文件必须以某种特定的方式存储数据,这种特定的方式称为文件结构。不同的文件有不同的结构。这里介绍由记录组成的文件。其基本结构有:字符(Character)、字段(Field)、记录(Record)、文件(File),它们之间的关系是:文件由记录组成,记录由字段组成,字段由字符组成。字符是数据的最小单位,而记录是Visual Basic数据处理的基本单位,文件就是记录的集合。
例如,一个学生信息的记录可以由学号、姓名、性别、年龄、班级、成绩字段等构成,而全班所有学生的记录就构成了一个学生信息文件。
根据不同的标准,文件可分为不同的类型。
根据数据性质,可分为程序文件和数据文件。
程序文件(program file)是可以由计算机执行的程序,包括源文件和可执行文件。在Visual Basic中,扩展名为.exe,.frm, .vbp, .vbg, .bas, .cls的文件都是程序文件;数据文件(data file)是指用来存放各种普通的数据,例如学生成绩等。这类数据必须通过程序文件来存取和管理。
同课章节目录