VBA里数组最基础的知识可以先看下数组Array里说的,是比较基础的一些东西。
1 定义数组
定义一个数组的时候,VBA编译器做了什么?
代码语言:javascript复制Sub Test()
Dim a1() As Byte
Dim a2() As Long
Dim a3() As String
Dim a4() As Variant
Dim p1 As LongPtr, p2 As LongPtr, p3 As LongPtr, p4 As LongPtr
p1 = VarPtrArray(a1)
p2 = VarPtrArray(a2)
p3 = VarPtrArray(a3)
p4 = VarPtrArray(a4)
Dim p1value As Long, p2value As Long, p3value As Long, p4value As Long
CopyMemory VarPtr(p1value), p1, 4
CopyMemory VarPtr(p2value), p2, 4
CopyMemory VarPtr(p3value), p3, 4
CopyMemory VarPtr(p4value), p4, 4
Debug.Print p1, p1value
Debug.Print p2, p2value
Debug.Print p3, p3value
Debug.Print p4, p4value
End Sub
'输出:
3074256 0
3074252 0
3074220 0
3074244 0
VarPtrArray得到的那个地址,也就是变量a1、a2、a3、a4的地址,这个和定义其他的数据类型的变量都一样,此时编译器仅仅是给变量分配了内存地址。
这个地址里面保存的数据就是数组的地址,不管定义一个什么类型的数组,在没有初始化数组容量的时候,变量地址里保存的数据都是0,也就是编译器还没有为数组申请任何的内存空间。
2 初始化数组
初始化一个数组的时候,VBA编译器做了什么?
代码语言:javascript复制Sub Test()
Dim a1() As Byte
ReDim a1(1) As Byte
Dim p1 As LongPtr
p1 = VarPtrArray(a1)
Dim p1value As Long
CopyMemory VarPtr(p1value), p1, 4
Debug.Print VBA.Hex(p1), VBA.Hex(p1value)
End Sub
'输出:
2CE950 C1774F8
使用ReDim将数组初始化后:
- 编译器申请了一块内存空间来保存数组的数据,这个空间的地址记作pvDatas。
- 编译器申请了一块内存空间来保存数组的信息,这个空间的地址就记作pSafeArray,并把这个空间的地址写入变量p1地址,并把pvDatas的值记录到pSafeArray后12的地址上。
3 数组的数据结构
官方介绍数组:一组顺序索引的元素。
数组在VBA里是一个数据类型,除了这一组顺序索引的元素之外,还有一个记录了数组一些信息的结构体SafeArray。
所以,上面的C1774F8指向的是SafeArray结构体,而并不是数组数据开始的地址:
代码语言:javascript复制Sub Test()
Dim a1() As Byte
ReDim a1(1) As Byte
Dim p1 As LongPtr
p1 = VarPtrArray(a1)
Dim p1value As Long
CopyMemory VarPtr(p1value), p1, 4
Dim sa As SafeArray
'获取SafeArrayBound之前的数据
CopyMemory VarPtr(sa.cDims), p1value, 16
ReDim sa.rgsabound(sa.cDims - 1) As SafeArrayBound
'根据维度再读取需要的数据
CopyMemory VarPtr(sa.rgsabound(0).cElements), p1value 16, sa.cDims * 8
Debug.Print VBA.Hex(p1), VBA.Hex(p1value), VBA.Hex(sa.pvDataas), VBA.Hex(VarPtr(a1(0)))
End Sub
'输出:
2CEA50 1F5D28F8 1890A4A0 1890A4A0
可以看到sa.pvDataas和VarPtr(a1(0))的地址是同一个地址,这个才是真正的数组数据开始的地方,SafeArray的作用就是记录一些数组的信息,长度、维度等,方便来操作数组。
这个的用法和String类型是类似的,主要是为了方便操作和处理,就在数据开始的前面记录了数据的长度信息。SafeArray记录的数据更多,而且记录的位置也不是在紧挨着数据开始的前面,而是另外开辟空间来记录。