目的:整理VBA变量相关的3个问题
- 变量的声明 (变量的定义)—-对比常量的定义
- 变量的作用域(生效范围)
- 变量的赋值 和 变量的默认值(如果没有赋值,那么默认值就是初值)
0 要讲变量,先讲常量 常量定义用 const
0.1 常量的定义
- 常量英文 constant
- 常量关键字 const
- 常量定义的语句
- const a1 =1
- const a2 as string = “hello”
0.2 常量和变量的区别:声明时马上赋值
- 常量必须定义的时候,马上赋值 ( 而变量不能这样)
- 常量关键字 const as
- 变量 关键字 dim as (类似的还有 static private public)
- dim 比较特殊,可以作为 模块级的声明,也可以作为过程级的声明
0.3 常量可以定义在模块内或过程内
如果时模块级,也可以用 public private等关键字,声明作用域
- 常量的声明可以在 模块的第一个sub 前,也可以在sub内
比如
Public Const a5 As String = “hello”
0.4 常量的测试
Const a1 = 1
Const a2 As Integer = 2
' dim Const a3 As Integer = 3 '语法报错,常量定义 只用 const 和 dim是对应的!
Dim a4 As Integer
Public Const a5 As String = "hello"
Sub testa101()
Const b1 = 10
Debug.Print "a1=" & a1
Debug.Print "a2=" & a2
Debug.Print "a5=" & a5
Debug.Print "b1=" & b1
End Sub
1 变量的声明 (就是变量的定义)
变量的声明就是变量的定义,先定义好变量,再使用是个好习惯
1.1 先声明好变量类型
- 初学时,可以先不管,不定义变量类型
- 但要明白,dim a 这种没声明的,其实都时隐式声明为了 dim a as variant (通用型变量)
- 初学之后,一定要先声明变量,加option explicit,不加这个遇到变量赋值问题不会报错。
- 声明语句 dim a as integer
- 特殊问题; dim i , j as integer 只有j 是int ,而i 是 variant
1.2 变量分类
- 基本型变量: integer string double float date
-
Boolean
-
Byte
-
Integer
-
Long
-
Currency
-
Single
-
Double
-
Date
-
String
- 通用性变量: variant ,默认就是通用变量,可以随时改变变量的类型
- 数组变量: dim arr1() as string
- 对象变量: 如 dictionary worksheet 等等,dim dict1 as object ,对象变量的赋值要用set
1.3 变量定义时不指定类型,默认都是 variant 通用型变量,很灵活,但是也很费
- dim a1
- dim a1 as variant
1.4 动态变量和静态变量,猜想英文原意?
- dim 是 dynamic? 动态变量?
- static 静态变量
1.5 不能在声明的变量同时赋值。变量需要先声明,后赋值。
Dim i As Integer
Dim j As Integer: j = 1 '错误代码,不能再模块最上面给变量赋值
Sub test100()
Debug.Print i
Debug.Print j
i = i + 1
j = j + 1
Debug.Print i
Debug.Print j
End Sub
1.5.1 特殊用法:相当于合并语句
(意义不大)
-
只能在过程内,因为变量只能在过程内赋值!
-
过程级变量,声明和赋值,可以用 :链家,相当于合并语句而已
- 声明+ 赋值语法 dim a as integer :a=1
- 声明后 赋值的标准语法
- dim a as integer
- a=1
Const k1 As Double = 3.1415926
Sub test100()
Dim i As Integer
Dim j As Integer: j = 1
Const k As Double = 3.1415926
Debug.Print i
Debug.Print j
Debug.Print k
Debug.Print k1
i = i + 1
j = j + 1
Debug.Print i
Debug.Print j
End Sub
2 变量初值与默认值
2.1 变量的初值
- 初值:是变量最初被赋予的值
- 一般要求变量赋值后才可以表达式运算
- 如python 会在表达式运算时,把变量 代换为 值,所以没赋值会报错!
2.2 变量的默认值
- 默认值:变量没被赋值时,默认的值(有的语言没有,会报错)
- VBA看起来是给变量赋予了默认值。
- 比如变量一般默认值为 “”
2.3 不同类型的变量,其默认值不同
赋值,初值,默认值(和类型有关)一般为0!
- integer 默认值 0
- string 默认值 “”
- long 默认值 0
- double 默认值 0.0
- object nothing 释放也时 set object = nothing
- 等等
2.4不同变量类型的默认值查询
https://blog.csdn.net/hpdlzu80100/article/details/80664914
2.5 为什么一般不能在声明变量时赋值呢?
- 声明时不能给变量赋值。(变量,静态变量都会被初始化,但是而常量并不会被初始化)
-
因为当过程开始运行时,所有的变量都会被初始化。
- 一个数值变量会初始化成 0,变长字符串被初始化成零长度的字符串 (“”),而定长字符串会被填满 ASCII 字符码 0 所表示的字符或是 Chr(0)。
3 变量的作用域(生效范围)
变量到底能再哪些地方生效,这个由2个因素决定
- 变量声明时,声明语句前面的关键字,比如 dim private public
- 变量声明时,所在的代码的层级。 特别是用 dim 这种灵活而模糊的
3.1 全局级作用域:
用 global / public 来声明在模块最前面!
(实测不生效!!!)
变量可以
跨工作簿/ 工程:
- 可以跨多个工作簿生效,有这样的变量吗?据说有
- 不能加Option Private Module 用global 和 public
-
我实测了不行,但据说是适用一个 EXCEL文件下,有多个工程的情况,没见过,暂时我测试没有看到可以跨工程的变量。
3.2 工程级作用域:用 global /public 来声明在模块最前面(过程外)
变量可以在整个工程内生效,可以跨这个工程内的模块
- 用public 关键字在模块的第1个过程之前声明的变量 (public不能用在过程内)
- 可以在多个模块内都生效
- public 声明的变量,丢失有两种可能,一种是你关闭了excel文件,另一种是你重新编辑了代码,其他情况都会保留的
特殊情况
- Option Private Module
- 加上这句话后,这个模块内的代码,只能再本工程内使用
- 并且,点开EXCEL的菜单宏,将看不见这些宏了。
3.3
模块级作用域 :用dim / private 来声明在过程外
- 用private 或 dim 在模块的第1个过程之前声明的变量,这时候 private 或 dim 此时等价。
- 差别是,dim 很模糊,可以用在过程内/过程外,而private 必须声明在过程外。
- 也就是用private a as string 声明的变量,是模块级变量,不能声明在过程里。
- 如果加了这句话 一定只是在模块内生效 !Option Private Module
3.4 过程级变量:用dim / static 来声明在过程内部!
- 用 static 或 dim 在过程内声明的变量,只在这个过程内生效。但是 static 和dim 声明的变量完全不同
- 差别是,dim 很模糊,可以用在过程内/过程外,static 声明的变量,只能定义在过程内。
- static a1 这样定义的变量,虽然一定是定义在过程内的,但是 static定义的变量在过程结束后并不会被释放,而是一直起作用到,过程所在的模块 重置 会重新启动。
- static a1 静态变量的作用域,还是过程内。但是生命周期在 模块重置/重启 前都一直生效。
3.5 变量定义时,VBA的特殊问题
- 作用域语句,是整行生效
- 但是 声明语句 dim as 语句不是,对单个变量生效
-
当相同名称的变量,多次以不同的作用域声明时,出现作用域冲突。这种情况,VBA 会自动以
就近原则
使用变量,即优先使用最近定义的变量。也就是一般会先使用过程内部的,更近的 - 比如
- 例子1: dim i , j as integer 只有j 是int ,而i 是 variant
- 例子2: Public a6, a7 As Integer ,这2个变量作用域都时public
4 变量生命周期
(生命期不完全等同于范围)
变量的生命周期 和 变量定义时的 作用域定义是 密切相关的,基本就是作用域外,就不再生效。
比如
- 全局级:excel 程序打开后都生效,除非EXCEL关闭,一直在内存里
- 工程级:当前的EXCEL表,工程,没有关闭就一直生效。
- 模块级:当前模块在运行就生效
- (反之,没运行 这个模块 就不生效)
- 过程级,过程执行时才开始生效,给其分配内存空间,过程执行结束就从内存释放
-
(反之,没运行 这个过程/函数 就不生效,想生效就得调用它! 这就是调用原因!)
- 过程级的例外:static 在过程执行后还是生效,将保留其值,直到模块重置或重新启动
- Static]定义的是静态变量,这意味着在过程结束后这个变量所占有的内存不会被回收,数据也不会被破坏,下次再调用该过程的时候,数据就依然存在。
5 过程得作用域 和 变量作用域
5.1 过程和函数的 默认作用域 public
-
比如过程和函数默认是 工程级作用域
-
实测,在sub 前加 public 可以,不加默认也时 public
-
sub 相当于 public sub “在workbook内有效
-
function 相当于 public function “在workbook内有效
5.2 过程/函数 也可以用 private 来声明
-
但是如果加 private 声明,就限制在只在模块内生效,无法被跨模块调用
-
如果定义为 private sub testsub1() “只在本模块内有效。
5.3 实测,过程/函数 好像无法 全局作用域?
-
实测,在sub 前加global 自动消失
5.4 过程/函数 声明为static
-
如果声明为静态
-
静态过程 static sub ,其内部所有变量都是 static 变量
5.5 变量的最终作用域 (两层 作用域 共同作用的结果)
- 过程/函数的作用域 和变量的作用域是两回事
-
但是 过程的属性 部分情况下会影响变量的作用域,如static sub
6 变量的调用
6.1 搞清楚,为什么要调用变量? 调用变量就得先调用其他过程/函数?
- 首先,过程级的变量是无法调用的,即使你调用那个过程。这可以认为是一种对过程级变量的的保护。
-
一般的过程变量,
都是过程,开始运行时才重新分配内存(重新初始化)
,过程结束就重置了,这也是一般变量都不允许在过程外赋值的原因。因为即时是一个 声明为 public 变量,在某个过程内的赋值,也只有运行这个过程时才生效。 - 而想要调用这个 public变量,在其他过程中的值,就必须调用那个 给这个 public变量赋值的过程,这就是过程调用。并且,只有1个程序调用另外一个程序过程中,才可以读到其他 程序的变量的值。
6.2 怎么调用? 调用变量的几个条件
变量如果想在多个程序之间调用
- 首先要声明为 非局部变量。
- 需要调用变量所在过程/函数,才能调用其他过程内的变量 (变量的赋值都在过程内,虽然变量可以声明在过程外)
6.3 调用常量
和调用变量是一样的,见下面的例子
6.4 变量的传递方式
情况1:为了调用其他程序的变量,而调用其他程序
本程序(主程序)调用其他程序(被调用程序)时,可以取到其他程序的参数
变量的调用,需要声明为非局部变量,且需要调用变量所在的代码,
情况2:为了给其他程序的主动传递变量,而调用其他程序,并获得回调
本程序(主程序)调用其他程序(被调用程序)时,可以把参数传给其他程序,并取得回调。而并调用的程序,被调用时必须传参数给它,否则无法正确调用
-
被调用的程序,可以设定2种传递方式,进而影响给主程序的回调值。
-
也就是会不会受影响,不由 主程序 决定!?
-
byref ,被调用程序 按地址传递,传回主程序的参数,回调时会受影响。
-
VBA默认按地址传递,方便管理?
-
byval ,被调用程序 按值传递,传回主程序的参数,回调时会不受改变。
按地址传递(ByRef),是指主程序直接将数据交给子过程(过程中定义传递方式),在过程中修改、调试、返回给主程序,主程序输出的是修改后的值。
例如:如果我在参数中设为ByRef,那么ByRef的这个变量我可以改变它的值
按值传递(Byval),是指将主程序的值(副本一份)给子过程,过程对副本操作,主程序输出的仍是原值。若想输出值传递后的值,可在子过程中设置输出。
6.5 还有其他各种调用
- 参数,变量得调用
- 过程/函数得调用
- 模块得调用
-
被调用结束前,是不会释放得. 也就是除非调用它们得 宿主程序释放,否则不会释放
7 实例
7.1 实例: 变量的成功调用(工程级作用域,跨模块调用)
举例:模块1代码
Public t1
Public t2
Global Const t3 As Integer = 777
Sub bbtest1()
Dim k1
t1 = 100
k1 = 1000
End Sub
Private Sub bbtest2()
Dim k2
t2 = 101
k2 = 1001
End Sub
模块2代码
- 要成功的调用需要满足2个条件
- 必须要先调用这个变量的过程/函数
- 这个变量,必须是非过程级的,模块级的/工程级的
- 如果 private 声明了一个过程,则这个过程只能被模块内调用,不能被跨到 模块2调用,这里会报错
Sub cctest1()
Debug.Print "t1=" & t1
Debug.Print "k1=" & k1
End Sub
Sub cctest2()
Call bbtest1
Debug.Print "t1=" & t1
Debug.Print "k1=" & k1
End Sub
Sub cctest3()
'Call bbtest2 '报错,因为bbtest2 是private 无法被其他模块的程序调用
Debug.Print "t1=" & t2
Debug.Print "k2=" & k2
End Sub
7.2 实例: 静态变量的成功调用
静态变量 static
- 静态变量,肯定都是过程级变量(局部变量),只能声明在过程内,函数内
- 静态变量的特点,是只要静态变量所在的模块,工程没关闭,静态变量都不会释放
- 不会释放是什么意思?也就是说 内存空间不会释放,变量不会被重置掉。
-
而一般的过程变量,
都是过程,开始运行时才重新分配内存(重新初始化)
,过程结束就重置了,这也是一般变量都不允许在过程外赋值的原因。因为即时是一个 声明为 public 变量,在某个过程内的赋值,也只有运行这个过程时才生效。 - 而想要调用这个 public变量,在其他过程中的值,就必须调用那个 给这个 public变量赋值的过程,这就是过程调用。并且,只有1个程序调用另外一个程序过程中,才可以读到其他 程序的变量的值。
- 被调用程序的声明周期,应该和主调程序生命周期一致。
- 利用这个可以做啥?
- 静态变量在程序已经不在它们的过程里时仍然不会丢失它们的内容
- static一个典型的用法,就是可以用来做累计值,反复调用其他程序时,自身的static变量一直在累计。
Private x
Private j
Sub Qtest1()
Static sum1
' sum1 = 0 '不能加这句, 这相当于循环开始进行了归0,VBA里静态变量就是要利用不赋初值的特性
Call Qtest2
sum1 = sum1 + x
Debug.Print "sum1=" & sum1
End Sub
Sub Qtest2()
x = 5
End Sub
Sub Qtest10()
Call Qtest20
sum1 = sum1 + j + k '这里的k,是用的本过程的,不是其他过程得k,也没有更高作用于的k
Debug.Print "sum1=" & sum1
End Sub
Sub Qtest20()
j = 5
k = 5 'k是过程变量,是无法传出去的
End Sub
7.3 实例: 常量的成功调用
- 常量基本和变量也是一样
- 按常量的 声明关键字的,定义在过程内,还是模块外,用的 dim ,private 还是 public global
Const x2 = "hi"
Sub testnew1()
Const x1 = "hello"
End Sub
Sub testnew2()
Call testnew1
Debug.Print "x1=" & x1
Debug.Print "x2=" & x2
End Sub