ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 12226|回复: 33

[分享] VBA数组进阶--典型编译/运行时错误之成因及解决办法

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2014-9-10 21:29 | 显示全部楼层 |阅读模式
本帖已被收录到知识树中,索引项:数组集合和字典

----------VBA数组典型编译/运行时错误之成因及解决办法----------
  
      如果错误理解声明、创建和使用数组的规则,将产生编译或运行时错误。本文探讨常见错误的产生原因及解决
办法。数组的各种限制或规则在不同Excel(VBA)版本中有一定差异,文中所及主要以2003(VBA 6.5)和2010(VBA
7.0)为测试对象.有些错误在2003版本以后得以修正,则不再列举了。
.
...........要顺利阅读本文,请先了解以下概念.............
  
数组数据类型与数组元素数据类型
    这两个概念是有差别的,一般来说数组代表一群具有相同数据类型的元素值,这种情况下两者数据类型是一致
的;但对数据类型为Variant的数组却并不一定如此.数据类型可以用TypeName或VarType函数返回。

数组的类型
    以维度来分: 有一维、二维、三维...数组.
    以数组数据类型分: 有字符型数组/固定字符长度型数组/Variant数组等等(不是指元素的数据类型).
    以创建途径不同分: 标量数组(本文探讨的类型)/控件数组/参数数组/内存数组等.
    以数组大小是否固定分: 动态数组/静态数组
    以数组有效范围分:全局(公用)数组、模块级数组、局部数组

.....................本文讨论的主题..........................

※声明全局数组错误         ※For Each语句循环读取数组元素值错误
※参数数组声明错误         ※数组数据传入工作表错误
※改变数组的大小错误      ※转置数组错误
※改变数组数据类型错误   ※工作表函数应用于日期类型数组错误
※数组赋值错误                ※过程中传递整个数组错误

评分

6

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:30 | 显示全部楼层
本帖最后由 liu-aguang 于 2014-9-10 21:43 编辑

一、试图声明全局(公用)数组出错
症状:
    在一个工作表对象模块中声明全局数组。
  1. Public arr()   '在一个工作表对象模块中声明全局数组出错.
  2. Sub test()
  3.    ReDim arr(9)
  4.    For i = 0 To 9
  5.       arr(i) = i
  6.    Next
  7. End Sub
复制代码
原因
    VBA可以通过Public或Private(Dim)在项目模块中声明全局数组或模块级数组,通过Private(Dim)在过程中声明
局部数组.但VBA不允许在工作表模块内创建全局数组.

办法1
   在项目标准模块的通用声明部分声明
  1. Public MyArray(1) As String
  2. '.............
  3. Sub FiArray()
  4.     MyArray(0) = "Hi": MyArray(1) = "Bye"
  5.     DisplayArray
  6. End Sub
  7. Sub DisplayArray()
  8.     For i = 0 To UBound(MyArray)
  9.         MsgBox MyArray(i)
  10.     Next
  11. End Sub
复制代码
方法 2
    使用参数数组功能
    可以使用参数数组来传递给过程的参数的数组。您不必定义过程时知道数组中的元素的数目。您可以使用参数
组关键字来表示一个参数数组。您必须将数组声明为 variant 类型的数组,它必须是过程定义中的最后一个参数。
Sub AnyNumberArgs(strName As String, ParamArray intScores() As Variant)
      Dim intI As Integer
      For intI = 0 To UBound(intScores())
         MsgBox strName & "'s Scores: " & intScores(intI)
      Next intI
   End Sub
Sub CallParamArrayRoutine
      AnyNumberArgs "Jamie", 10, 26, 32, 15, 22, 24, 16
      AnyNumberArgs "Kelly", "High", "Low", "Average", "High"
End Sub


TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:31 | 显示全部楼层
本帖最后由 liu-aguang 于 2014-9-10 22:07 编辑

二、参数数组声明错误
症状1:
    将参数数组声明为一个非Variant数组出错:
   
    Sub Mysub(paramarray VarArg() as Integer) '声明为Integer类型出错
原因:   
    ParamArray数组的每个元素可以是不同的数据类型,然而数组本身必须声明为Variant的数组.
   
    Sub Mysub(ParamArray VarArg() As Variant)
症状2:
    在模块声明部分使用了Deftype关键字,而函数过程参数数组没有指定为任何数据类型而出错。

  1. DefInt A-K
  2. Function bTest(ParamArray aVarArg())  '这时数组aVarArg不再是默认的Variant类型
  3.     bTest = aVarArg(0)
  4. End Function
  5. Sub Test()
  6.     bTest 1, 2, 3
  7. End Sub
复制代码
办法:
   将参数数组aVararg显示设置为Variant.
   
Function bTest(ParamArray aVarArg() As Variant)



TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:32 | 显示全部楼层
本帖最后由 liu-aguang 于 2014-9-10 22:43 编辑
复制代码
三、改变数组的大小出错
症状
    试图用Redim重新声明静态数组的大小。
错例1:
  1. Sub test()
  2.     Dim a(1) As Integer
  3.     ReDim a(2) '错误发生在这里.
  4. End Sub
复制代码

错例2:
  1. Sub FirstOne()
  2.     Dim FixedArr(25) As Integer
  3.     NextOne FixedArr() '将之传给过程NextOne
  4. End Sub
  5. Sub NextOne(someArr() As Integer)
  6.     ReDim someArr(35)    '发生错误10
  7.     '....
  8. End Sub
复制代码

原因
    Redim可反复改变动态数组大小(元素和维数的数目),但是不能改变静态数组的大小。
  
办法1:
    将数组用Redim(如果是在过程内声明数组)声明为动态而非固定大小;或在声明时不指定元素数(如果是模块级
别中声明数组)

  1. Sub test()
  2.     Dim a() As Integer   '声明一个动态数组
  3.     ReDim a(1 To 10, 1 To 2)  '初始化为一个二维数组
  4.     ReDim a(9)                '修改为一维数组
  5. End Sub
复制代码
办法2
    Redim将原来数组中的数据全部清除,如果要保留原来数据,可用Redim Preserve语句.
  1. Sub test()
  2.     Dim a() As String
  3.     ReDim a(1)
  4.     a(0) = "A": a(1) = "B"
  5.     ReDim Preserve a(2)
  6.     a(2) = "C"
  7.     MsgBox a(0) & a(1) & a(2)
  8. End Sub
复制代码
讨论:
     Redim Preserve只能重新定义最末维的大小,并且不能改变维数数目.
     Redim Preserve可以保留调整数组大小时的数组内容,但其操作不快。为了调整数组大小,VBA要取一块内存用
于新数组,并在指定Perserve的情况下将原数组中的所有项目复制过去,最后还要释放原数组的内存。最好不用
Redim Preserve。替代办法有:1.使用集合。2.链接清单之类的动态数据结构。3.可以一次加进一块项目来调整数
组大小。即不是每次需要更多项目时都调整数组大小,而是一次加进一个较大项目数,只在超出时才调整数组大小
。项目加入之后,可以最后再调用Redim Preserve正确地调整数组长度。

错例3:
    试图改变动态数组大小出错
    对模块层次的动态数组,要重新确定大小,而某一个元素已经作为参数传给了过程.
Dim ModArray() As Integer '创建一个模块层次动态数组
Sub AError()
    ReDim ModArray(1 To 3) As Integer
    Test ModArray(1) '传递动态数组的元素给Test过程
End Sub
Sub Test(someint As Integer)
    ReDim ModArray(1 To 10) As Integer '错误发生
End Sub


TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:33 | 显示全部楼层
本帖最后由 liu-aguang 于 2014-9-11 08:41 编辑

四、改变数组数据类型出错

症状:
    试图使用redim重新声明数组的数据类型
错例1:
  1. Sub test()
  2.     Dim abc()
  3.     ReDim abc(2) As String
  4. End Sub
复制代码

错例2:
  1. Sub test1()
  2.    Dim ar() As String
  3.    ReDim ar(2) As Integer
  4. End Sub
复制代码
原因:
    Redim不能用来改变数组数据类型。与标量变量不同,VBA没有提供直接改变数组数据类型的函数.
办法1:
    请声明一个所要数据类型的新数组,然后使用转换函数将原来数组中的各元素转换为新数组的数据类型,最后赋与新数组中对应的元素.
办法2:
    也可将此数组放在Variant变量中。这可以用一个简单的赋值式来完成:
    Dim Myvar As Variant
    Myvar = oldArray()
    这将创建一个包含与原来数组类型相同之数组的 Variant。然后可以将任何有效 VarType 的变量赋予 variant 中的数组元素。

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:33 | 显示全部楼层
本帖最后由 liu-aguang 于 2014-9-11 08:57 编辑

八、转置数组出错

症状:
    数组元素字长超过一定值导致转置出错
  1. Sub test()
  2.     Dim x, y
  3.     ReDim x(1 To 2, 1 To 2)
  4.     x(1, 1) = String(1000, "a")
  5.     x(1, 2) = String(100, "b")
  6.     x(2, 1) = String(50, "c")
  7.     x(2, 2) = String(10, "d")
  8.     y = Application.Transpose(x) '错误发生在这里
  9. End Sub
复制代码
原因:
转置,数组元素字符串长度不能超过255个。

办法1:
    构建一个自定义的转置函数。

  1. Sub Transpose1_Solve()
  2.     Dim arr() As Variant
  3.     Range("A1:B1") = Array("A", String(256, "B"))
  4.     arr = Range("A1:B1")
  5.     Range("A3:A4") = TransposeDim(arr)
  6. End Sub

  7. Function TransposeDim(vArrTrans As Variant) As Variant
  8.     Dim X As Long, Y As Long
  9.     Dim XUpper As Long, YUpper As Long
  10.     Dim XLower As Long, YLower As Long
  11.     Dim tempArray As Variant
  12.     XUpper = UBound(vArrTrans, 2)
  13.     XLower = LBound(vArrTrans, 2)
  14.     YUpper = UBound(vArrTrans, 1)
  15.     YLower = LBound(vArrTrans, 1)
  16.     ReDim tempArray(XLower To XUpper, YLower To YUpper)
  17.     For X = XLower To XUpper
  18.         For Y = YLower To YUpper
  19.             tempArray(X, Y) = vArrTrans(Y, X)
  20.         Next Y
  21.     Next X
  22.     TransposeDim = tempArray
  23. End Function
复制代码
办法2:
    在2010以上版本中,如果数组类型是字符串类型数组,则不受限制。

  1. Sub test()
  2.     Dim x, y
  3.     ReDim x(1 To 2, 1 To 2) As String
  4.     x(1, 1) = String(1000, "a")
  5.     x(1, 2) = String(100, "b")
  6.     x(2, 1) = String(50, "c")
  7.     x(2, 2) = String(10, "d")
  8.     y = Application.Transpose(x)
  9. End Sub
复制代码


TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:34 | 显示全部楼层
本帖最后由 liu-aguang 于 2014-9-11 08:46 编辑

五、数组赋值错误

症状1:
    试图一次性赋与所有元素相同值
  1. Sub test()
  2.     Dim VarDate() As Integer
  3.     VarDate = 5
  4. End Sub
复制代码
原因:
    数组中的各元素必须个别指定其值。
症状2:
    试图将整个数组指定给另一个数组
  1. Sub test()
  2.    Dim Arr1()
  3.    Dim Arr2(9) As String
  4.    Arr1 = Arr2   '错误发生在这里
  5. End Sub
复制代码
原因:
    想要将一个数组赋值给另一个数组,必须确定接收数组是动态数组,并且类型匹配。
办法1:
   将整个数组传递给一个类型相同的动态数组
  1. Sub test1()
  2.   Dim Astr1() As String
  3.   Dim Astr2(9) As String
  4.   Astr1 = Astr2
  5. End Sub

  6. Sub test2()
  7.   Dim arr()
  8.   'arr = Range("a1:d3") '或
  9.   arr = ActiveSheet.Range("a1:d3").Value
  10. End Sub
复制代码
讨论:
    工作表的区域(Range)的Value(包括Formula/FormulaR1C1等)是以二维数组形式组织的。它是一个静态Variant数组,所以在Test2过程中可以直接传递给一个动态的Variant数组(arr())。
    注意的是在【ActiveSheet.Range("a1:d3").Value】中,Value属性是不能省略的。Range("a1:d3")与ActiveSheet.Range("a1:d3")表达方式内部组织不同,前者是一个Range对象,默认属性是Value,可以省略;而后者被组织为一个Variant变量包含Range对象,而Variant变量没有默认属性,必须显示表达Value。
办法2(推荐):
    可以将整个数组放在一个Variant变量中,使一个Variant变量包含整个数组:
  1. Sub test()
  2.   Dim Astr1    '声明一个Variant变量
  3.   Dim Astr2(9) As String
  4.   Astr1 = Astr2
  5. End Sub

  6. Sub test()
  7.   Dim arr      '声明一个Variant变量
  8.   'arr = Range("a1:d3") '或
  9.   arr = ActiveSheet.Range("a1:d3")
  10. End Sub

  11. Sub test()
  12.   Dim arr     '声明一个Variant变量
  13.   arr = Array("A", String(25, "B"))
  14. End Sub
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:35 | 显示全部楼层
本帖最后由 liu-aguang 于 2014-9-11 08:51 编辑

六、For Each语句循环读取数组元素值出错

错例1:
  1. Sub test()
  2.   Dim ar(1) As String * 10
  3.   ar(0) = String(3, "a")
  4.   ar(1) = String(10, "b")
  5.   For Each k In ar '错误发生在这里
  6.     Debug.Print k
  7.   Next
  8. End Sub
复制代码
  1. Type Carr
  2.     FirstName As String
  3. End Type

  4. Sub test()
  5.   Dim ar(1) As Carr
  6.   ar(0).FirstName = String(3, "a")
  7.   ar(1).FirstName = String(10, "b")
  8.   For Each k In ar '错误发生在这里
  9.     Debug.Print k
  10.   Next
  11. End Sub
复制代码

原因:
自定义类型数组和固定长度字符串的数组不能用For Each。这两类数组的元素控制变量不是Variant。

办法:
用for i语句循环。



TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:36 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 liu-aguang 于 2014-9-11 08:52 编辑

七、数组数据传入工作表出错

症状:
    向2003工作表传入数组数据出错
  1. Sub test()
  2.     Dim x
  3.     ReDim x(1 To 2, 1 To 2)
  4.     x(1, 1) = String(2000, "a")
  5.     x(1, 2) = String(100, "b")
  6.     x(2, 1) = String(50, "c")
  7.     x(2, 2) = String(10, "d")
  8.     Range("a1:b2") = x  '错误在这里
  9. End Sub
复制代码
原因:
    当满足以下条件之一,则可能发生此问题:
    在2003中,数组长度超过1823个字符长度(VBA6.5超过911);
    在2007中,数组长度超过8203个字符长度;     
    在2010以上,无此限制。
办法:
    循环数组,逐个单元格填充。

点评

Excel2010对单元格的字符长度依然是有限制的,特别是输入公式的时候限制更严  发表于 2015-3-1 15:16
【数组长度】简略描述不正确、应更正为【数组中元素的字符串长度】  发表于 2014-9-11 10:07

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-9-10 21:37 | 显示全部楼层
本帖最后由 liu-aguang 于 2014-9-11 08:59 编辑

九、工作表函数应用于日期类型数组出错

症状:
    日期类型的一元数组求最值时,结果始终为0.
  1. Sub Date_Limit()
  2.     Dim arrDate(1 To 2) As Date
  3.     arrDate(1) = "2008-08-08"
  4.     arrDate(2) = Date
  5.     Debug.Print Application.Max(arrDate)
  6. End Sub
复制代码
原因及解决办法:
    日期类型数组应用于工作表函数Max/Min、Large、Small、Match等,必须首先把日期类型转换为Double。
  1. Sub Date_Solve()
  2.     Dim arrDate(1 To 2) As Date
  3.     arrDate(1) = "2008-08-08"
  4.     arrDate(2) = Date
  5.     Debug.Print Format(Application.Max(arrDate(1), arrDate(2)), "yyyy-mm-dd")
  6. End Sub

  7. Sub Date_Solve2()
  8.     Dim arrDate(1 To 2) As Date
  9.     Dim arr(1 To 2) As Double
  10.     arrDate(1) = "2008-08-08"
  11.     arrDate(2) = Date
  12.     arr(1) = arrDate(1)
  13.     arr(2) = arrDate(2)
  14.     Debug.Print Format(Application.Max(arr), "yyyy-mm-dd")
  15. End Sub
复制代码

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

关闭

最新热点上一条 /1 下一条

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-4-26 09:07 , Processed in 0.039022 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表