vb 指针的操作

 
VB指针教程1

'先理解下变量的地址与指向这个地址的值。xx省xx市xx街xx号就是一个地址,
'指向这个地址的值就是这个地址就有什么。比如这个地址是张三家的地址,
'张三的家有什么呢?这个有什么就是这个地址所指向的值。

'VB没用操作指针的语言特性,因此需要通过WinAPI函数来进行操作,下面我们将
'
演示一个非常简单的指针操作,让大家在VB使用指针有个基本的了解。
'
CopyMemory函数参数
'
CopyMemory是WinAPI函数,为了演示我把其修改成了两个版本,
'
版本1:CopyMemory1
'
版本2:CopyMemory2
'
版本1Destination及Source的参数是按照引用传递的(ByRef)
'
版本2Destination及Source的参数是按照值传递的(ByVal)
'
参数说明:
'
Destination
'
要复制内存块的目的地址?
'
Source
'
要复制内存块的源地址?
'
Length
'
指定要复制内存块的大小,单位为字节
'
返回值
'
该函数为VOID型,没有返回值。
Private Declare Sub CopyMemory1 Lib "kernel32" Alias "RtlMoveMemory" _
        (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

Private Declare Sub CopyMemory2 Lib "kernel32" Alias "RtlMoveMemory" _
        (ByVal Destination As Any, ByVal Source As Any, ByVal Length As Long)

'在Basic语言演变成QBasic,然后到Visual Basic之前,VarPtr函数就已经存在了。
'
开始,这个函数存在于VB运行库1.0版中。VarPtr函数的作用获取变量的地址
Private Sub Form_Click()
    Dim long1 As Long
    Dim long2 As Long
    long1 = 10
    '版本1的CopyMemory
    CopyMemory1 long2, long1, 4
    MsgBox long2
    '版本2的CopyMemory
    Dim long3 As Long
    CopyMemory2 VarPtr(long3), VarPtr(long1), 4
    MsgBox long3
End Sub


-----------------------------------------------------



'我们先理解一下数组的概念,数组是由一组连续的内存区域构成
'
比如有一排连续的房间,每个房间都有一个编号,这个编号也是连续的。
'
例如房间1、房间2、...房间n,这个n就是房间的编号,如果我对你说“请”到房间2
'
拿一本书给我,那么你完成这个任务首先就是找到房间2,然后进入房间2拿到这本书。
'
显而易见这个编号实际上就是地址
'
那么,如果我们取得数组的首地址,那么我们就可以根据地址对数组进行各种操作。
'
C语言提供了取得数组地址的语言特性,而VB则没有这样的语言特性,但我们依然可以
'
通过API得到数组的地址。我们先来认识下这一个DLL--Msvbvm60.dll,这个是vb6.0
'
的程序运行库,其就提供了一个可以获得数组地址的API,它的api声明如下:
Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" _
        (ptr() As Any) As Long
        
'CopyMemory API请参照《VB指针教程1》,地址:http://bbs.bccn.net/thread-305122-1-1.html
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
       (Destination As Any, Source As Any, ByVal Length As Long)

Private Sub Form_Load()
    Dim arr(1 To 3As Integer
    For i = 1 To 3
        arr(i) = i * 10
    Next
    Dim arraddr As Long
    '取得arr数组的首地址
    arraddr = VarPtrArray(arr)
    Dim arr1 As Integer, arr2 As Integer, arr3 As Integer
    CopyMemory arr1, ByVal arraddr, 2
    CopyMemory arr2, ByVal arraddr + 22
    CopyMemory arr3, ByVal arraddr + 42
    List1.AddItem "arr(1)=" & arr1
    List1.AddItem "arr(2)=" & arr2
    List1.AddItem "arr(3)=" & arr3
End Sub
'观察运行的结果,显然不是我们所预期的,为什么呢?这是因为VB数组的存储结构不同于我们先前所说的。
'
VB数组的存储结构是使用一种叫做SafeArray的结构,而VarPtrArray(arr)取得的即是指向SafeArray结
'
构的指针,我们现在得先去了解下SafeArray结构:
'
Private Type SafeArray
'
    cDims As Integer     '指向数组维数的指针
'
    fFeatures As Integer '用来描述数组如何分配和如何被释放的标志
'
    cbElements As Long   '指向数组元素所占的字节数的指针
'
    cLocks As Long       '一个计数器,用来跟踪该数组被锁定的次数
'
    pvData As Long       '指向数组内一个元素的指针,最重要的部分
'
    cElements As Long    '数组元素的个数
'
    lLbound As Long      '数组的下限
'
End Type
'
'以下是fFeatures的常量标志
'
Private Const FADF_AUTO         As Integer = &H1   '在堆栈中执行的分配
'
Private Const FADF_STATIC       As Integer = &H2   '静态分配
'
Private Const FADF_EMBEDDED     As Integer = &H4   '在结构中创建
'
Private Const FADF_FIXEDSIZE   As Integer = &H10   '不能改变数组大小
'
Private Const FADF_RECORD      As Integer = &H20   '记录容器
'
Private Const FADF_HAVEIID     As Integer = &H40   '有IID 身份标记 数组
'
Private Const FADF_HAVEVARTYPE As Integer = &H80   'VT 类型数组
'
Private Const FADF_BSTR       As Integer = &H100   'BSTR数组
'
Private Const FADF_UNKNOWN    As Integer = &H200   'IUnknown* 数组
'
Private Const FADF_DISPATCH   As Integer = &H400   'IDispatch* 数组
'
Private Const FADF_VARIANT    As Integer = &H800   'VARIANTs数组
'
Private Const FADF_RESERVED  As Integer = &HF008   '余留,将来使用

   
Private Sub Command1_Click()
   List2.Clear
   List3.Clear
   Dim arr(1 To 3As Integer
   For i = 1 To 3
       arr(i) = i * 10
   Next
   '指向SafeArrayld结构的指针
   Dim SafeArrayldPoint As Long
   
   '把arr数组的首地址复制到SafeArrayldPoint
   CopyMemory SafeArrayldPoint, ByVal VarPtrArray(arr), 4
   
   '数组的维数
   Dim dims As Integer
   CopyMemory dims, ByVal SafeArrayldPoint, 2
   List2.AddItem "数组的维数:" & dims
   
   Dim elements As Long
   CopyMemory elements, ByVal SafeArrayldPoint + 44
   List2.AddItem "数组元素所占的字节数:" & elements
   
   Dim eCount As Long
   CopyMemory eCount, ByVal SafeArrayldPoint + 164
   List2.AddItem "数组元素的个数:" & eCount
  
   Dim lBd As Long
   CopyMemory lBd, ByVal SafeArrayldPoint + 204
   List2.AddItem "数组的下限:" & lBd
    '读取数组的值
   Dim arraddr As Long
   CopyMemory arraddr, ByVal SafeArrayldPoint + 124
   Dim arr1 As Integer, arr2 As Integer, arr3 As Integer
   CopyMemory arr1, ByVal arraddr, 2
   CopyMemory arr2, ByVal arraddr + 22
   CopyMemory arr3, ByVal arraddr + 42
   List3.AddItem "arr(1)=" & arr1
   List3.AddItem "arr(2)=" & arr2
   List3.AddItem "arr(3)=" & arr3
   '修改数组的值
   CopyMemory ByVal arraddr, 132
   CopyMemory ByVal arraddr + 2282
   MsgBox "arr(1)=" & arr(1)
   MsgBox "arr(2)=" & arr(2)
   MsgBox "arr(3)=" & arr(3)
    
End Sub

------------------------------------------------------------


'如果遇到不会的知识点请参考上面的教程
'
这一节我们来探讨下VB的字符串及字符串指针。
'
VB的字符串类型其实是BSTR类型,是"Basic STRing"的简称,微软在COM/OLE中定义的标准字符串数据类型。
'
BSTR类型的结构如下
Private Type S_String
    WordLen As Long    '存放字符串的字节数
    pData() As Integer '存放指向字符串的指针(即地址)
    WordEnd As Integer '存放结束字符(即chr(0))
End Type

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

'字符串在VB中的存储结构如下


Private Sub Form_Click()
    'StrPtr函数:返回真正的UNICODE字符串缓冲区的地址
    Dim str As String
    str = "any string"
    Dim i As Long
    Print "VarPtr(str)=" & VarPtr(str) & " 字符串变量的地址"
    Print "StrPrt(str)=" & StrPtr(str) & " 字符串的地址"
   
    CopyMemory i, ByVal VarPtr(str), 4
    Print "i=" & i & " 是字符串变量的地址所指向的值,它的值刚好是字符串的地址。"
    Print "从运行结果可以看出字符串变量的地址 VarPtr(str) 与字符串地址 StrPtr(str) 的区别。"
    Print "就是说字符串变量的地址VarPtr(str)存放着一个地址,这个地址就是字符串的地址,等价于StrPtr(str)"
    Print "那么我们就很容易理解为什么利用CopyMemory函数复制一个字符串到另一个字符串变量时参数是按值传递的"
    Print "读者可能会觉得奇怪,按值传递不就是把字符串传入去?CopyMemoy参数不是要求我们传递地址入去吗?"
    Print "没错,但不要忘记字符串的本质就是一个指针,指针就是地址。"
    Dim s2 As String
    s2 = String$(Len(str), 0)
    CopyMemory ByVal s2, ByVal strLen(str)
    Print "s2=" & s2
   
    Dim strLen As Long
    CopyMemory strLen, ByVal StrPtr(str) - 44
    Print "strLen=" & strLen; " 这是字符串的字节数"
    Print "从结果可以看出是与字符串的存储结构对应的"
    Print "利用这个性质,我们以模拟一下LenB函数和Len函数,分别为MyLenB、MyLen"
    Print "MyLenB(str)=" & MyLenB(str)
    Print "MyLen(str)=" & MyLen(str)
   
    Print "我们现在验证一下字符串存储结构中的WordEnd所存的是不是一个结束字符chr(0)"
    Dim we As Integer
    CopyMemory wn, ByVal StrPtr(str) + LenB(str), 2
    Print "we=" & we
End Sub

Private Function MyLenB(str As StringAs Long
     CopyMemory MyLenB, ByVal StrPtr(str) - 44
End Function

Private Function MyLen(str As StringAs Long
    CopyMemory MyLen, ByVal StrPtr(str) - 44
    MyLen = MyLen / 2
End Function
此代码是《VB真是想不到系列之三:VB指针葵花宝典之函数指针》的配套代码。 本系列文章可见: http://www.csdn.net/develop/list_article.asp?author=AdamBear 本代码主要是用来谈函数指针VB内部的应用之一,给出了qsort和ShellSort的实现。其中ShellSort完全是取自1998年5月VBPJ的Black Belt专栏里的源代码,可以说本文的思想基本上也来自这篇专栏文章。 ShellSort提共了三种不同的实现方法,分别是如下: PolySort1: 用Variant和对象缺省属性来比较。 PolySort2:用ISortable接口的多态对象技术来实现 PolySort3:用函数指针强制回调技术来实现。 分别运行一下这三个程序,可以发现用函数指针是最快的。值得一提作者的钻研精神,完全在VB里实现同一种算法完全三种不同的实现,而且一个比一个好,我非常佩服。 我原以为qsort应该会比它快不少,从算法上来说是这样,不过做出来才发现,要在VB里做出比它快的qsort很难,即使经过了仔细的优化。这是因为qsort的实现上比shellsort复杂,在C里多几次比较、多几次无用的移动影响不大,但在VB里多用一次API回调的Compare、多用一次CopyMemory都是很大的开销。而且qsort要嵌套调用(不嵌套在VB里也慢),我们还要尽量节约堆栈,虽然1M的默认堆栈大小可以被扩充(有相邻的空闲空间时),但是我们依然要考虑可能存在的溢出,所以我做的qsort仅两个参数,两个局部Long型变量。大家可以参考一下我最终的qsort的源代码。 见QSort工程里的basQSort模块,有详细注释。 虽然qsort还是比shellsort慢得多,但是可以说qsort已经进行了较好的优化。可见在VB里一个算法好不好,不能仅仅从理论上看,一个差一点但实现简单的算法和一个好一点但实现上复杂的算法在VB里谁好谁坏很难说。所以从实践意义上,ShellSort的确是个不错的算法。 无论ShellSort还是qsort,它们都还可以更加快,我在文章里说过,那必须要Hack一下SafeArray。 本系列第四篇文章《VB真是想不到系列之三:VB指针葵花宝典之数组指针》里再谈,这篇文章很快就会出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值