【原创】VBA学习笔记(16)VBA的变量的声明, 作用域(生效范围),赋值(默认值)

  • Post author:
  • Post category:其他


目的:整理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

  1. Boolean

  2. Byte

  3. Integer

  4. Long

  5. Currency

  6. Single

  7. Double

  8. Date

  9. 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个因素决定

  1. 变量声明时,声明语句前面的关键字,比如 dim  private  public
  2. 变量声明时,所在的代码的层级。 特别是用  dim 这种灵活而模糊的

3.1 全局级作用域:

用 global / public 来声明在模块最前面!

(实测不生效!!!)




变量可以

跨工作簿/ 工程:


  1. 可以跨多个工作簿生效,有这样的变量吗?据说有
  2. 不能加Option Private Module 用global 和 public


  3. 我实测了不行,但据说是适用一个 EXCEL文件下,有多个工程的情况,没见过,暂时我测试没有看到可以跨工程的变量。


3.2 工程级作用域:用 global /public 来声明在模块最前面(过程外)



变量可以在整个工程内生效,可以跨这个工程内的模块

  1. 用public 关键字在模块的第1个过程之前声明的变量 (public不能用在过程内)
  2. 可以在多个模块内都生效
  3. public 声明的变量,丢失有两种可能,一种是你关闭了excel文件,另一种是你重新编辑了代码,其他情况都会保留的

特殊情况

  1. Option Private Module
  2. 加上这句话后,这个模块内的代码,只能再本工程内使用
  3. 并且,点开EXCEL的菜单宏,将看不见这些宏了。


3.3

模块级作用域 :用dim / private 来声明在过程外

  1. 用private 或 dim 在模块的第1个过程之前声明的变量,这时候  private 或 dim 此时等价。
  2. 差别是,dim 很模糊,可以用在过程内/过程外,而private 必须声明在过程外。
  3. 也就是用private a  as string 声明的变量,是模块级变量,不能声明在过程里。
  4. 如果加了这句话 一定只是在模块内生效 !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个条件
  1. 必须要先调用这个变量的过程/函数
  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



版权声明:本文为xuemanqianshan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。