|
楼主 |
发表于 2013-3-31 18:00
|
显示全部楼层
本帖最后由 liucqa 于 2013-10-21 13:35 编辑
九、关于指针:谈谈VarPtr和StrPtr
这两个函数在微软的文档中是找不到帮助信息的。微软并不推荐我们使用这两个函数,因为在VB公开的文档中,这个编程语言并不支持指针。
然而,活用这两个函数,可以让VB在一定程度上具备指针的功能。特别是在做API函数调用的时候,我们必须通过这两个函数传递参数的指针。
下面看一个例子- Sub testStrPtr()
- Dim lng As Long
- Dim str As String, str1 As String
- Dim b(1 To 10) As Byte
- Dim sp As Long, vp As Long
- str = "ABC"
- sp = StrPtr(str) 'sp是字符串首地址即字符A在内存中的地址
- Debug.Print "字母A的内存地址:" & sp
- vp = VarPtr(str) '保存str指针变量的内存地址(str是指向BSTR字符串"ABC"的指针)
- Debug.Print "指向BSTR字符串""ABC""的指针的内存地址" & vp
- CopyMemory lng, ByVal vp, 4 '将vp指向的内容拷贝4个字节到lng。
- Debug.Print vp & "指向的内容是否是""A""在内存中的实际地址" & sp & ":" & (lng = sp)
- End Sub
复制代码 在这个例子中,我们很容易通过CopyMemory函数来检查BSTR风格的字符串在内存中的存储方式。
首先,我们通过StrPtr得到字符串"ABC”在内存中的实际存放地址。然后,我们通过VarStr得到str变量的指针地址(由于Dim定义的BSTR类型实际是一个指向BSTR字符数组的指针,所以这个Str变量本身应该有一个地址,而这个地址里面存放的数据才是指向"ABC"的实际地址)。最后,我们通过CopyMemory函数,将Str指向的地址复制到变量lng里面,通过与之前StrPtr获得的地址相比较,可以发现BSTR变量是一个指针,而这个指针指向的是字符串"ABC"的实际地址。
这里注意CopyMemory函数的使用,ByVal vp指的是拷贝vp变量里面的数值指向的地址中的数据。如果去掉ByVal就变成了拷贝变量vp在内存中的地址了。也就是说,我们的目的是拷贝vp变量记录的内存地址给lng,而不是拷贝vp变量本身在内存中的地址给lng。
再看两个例子,体会指针操作的奇妙之处!
第一个例子:- Sub TestBSTRRef()
- '替换BSTR变量指向BSTR字符串的指针,从而修改BSTR变量的字符串
- Dim vpstr As Long, vpbyte As Long, spstr As Long, str$, b(13) As Byte
- b(0) = 88 '随便写的数字,当作BSTR的字符串长度
- b(1) = 0
- b(2) = 0
- b(3) = 0
- b(4) = 66 'B
- b(5) = 0
- b(6) = 66 'B
- b(7) = 0
- b(8) = 65 'A
- b(9) = 0
- b(10) = 65 'A
- b(11) = 0
- b(12) = 0 'BSTR结束标记
- b(13) = 0 'BSTR结束标记
- str = "MNP"
- spstr = StrPtr(str) '记录指针
- CopyMemory ByVal VarPtr(str), VarPtr(b(4)), 4 '把b(4)的地址,拷贝到BSTR变量里面,当作新的指针
- 'CopyMemory ByVal VarPtr(str), (VarPtr(b(0)) + 4), 4
- MsgBox "长度是:" & Len(str) & vbCrLf & "字符串是" & str '输出长度44,字符串BBAA
- CopyMemory ByVal VarPtr(str), spstr, 4 '还原,避免内存泄漏,否则在回收内存时会有一定的概率崩溃
- End Sub
复制代码 这个例子,先构建了一个符合BSTR规范的字节数组,然后把指向str字符串的BSTR变量里面的指针,改成指向构建的字节数组,我们就会看到一个4个字符的字符串显示的长度却是44(88/2),这就非常准确的解释了BSTR字符串的内存结构。这段代码因为抛弃了之前分配的字符串,如果不还原,可能会导致Excel崩溃关闭。
'CopyMemory对不同形式参数的理解:
'(1) 传一个变量给pSource,那么源地址就是变量所在的地址
'(2) 以ByVal形式传一个变量给pSource,那么源地址就是变量的值
'(3) 字符串变量的值是个指针,指向字符串缓冲区的地址,也就是StrPtr(String1)。
' 因此,以ByVal形式传一个字符串变量给pSource,那么源地址就是字符串变量的值,也就是字符串缓冲区的地址。
另一个例子:- Sub TestBSTRVal()
- '修改BSTR变量指向的存储BSTR字符串的内存中的数据,从而修改BSTR变量的字符串
- Dim str$, b(13) As Byte
- b(0) = 88 '随便写的数字
- b(1) = 0
- b(2) = 0
- b(3) = 0
- b(4) = 66 'B
- b(5) = 0
- b(6) = 66 'B
- b(7) = 0
- b(8) = 65 'A
- b(9) = 0
- b(10) = 65 'A
- b(11) = 0
- b(12) = 0
- b(13) = 0
- str = "MNP"
- CopyMemory ByVal (StrPtr(str) - 4), b(0), 14 '从b(0)的地址开始的内存拷贝14个字节到BSTR的字节数组内存地址开始的内存里面
- MsgBox "长度是:" & Len(str) & vbCrLf & "字符串是" & str '输出长度44,字符串BBAA
- End Sub
复制代码 这个例子,先构建一个BSTR标准的字节数组,然后把这个字节数组的内容拷贝到BSTR变量指向的存放str字符串的内存中,从而替换掉了原来的字符串内容,显示的是字节数组构建的BSTR字符串。这个操作实际上破坏了内存原有的数据(从占用3个字符空间的内存变成了占用4个字符空间的内存),如果在存放str字符串内存的后面有其他有用的数据的话,这个数据就会被破坏。所以,此类操作极易造成数据丢失,严重会导致程序崩溃!这正是指针的危险之处!
交换两个字符串最快的方法- Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
- Sub testSwapStr()
- Dim StrA As String, StrB As String, pTmp As Long
- StrA = "abcd"
- StrB = "efghjik"
- 'CopyMemory pTmp, ByVal VarPtr(StrA), 4 '把StrA变量的指针(字符串的实际内存地址),赋值给pTmp
- 'CopyMemory ByVal VarPtr(pTmp), ByVal VarPtr(StrA), 4 '临时变量的第二种赋值方法
- pTmp = StrPtr(StrA) '临时变量的第三种赋值方法,这三种方法效果相同
- CopyMemory ByVal VarPtr(StrA), ByVal VarPtr(StrB), 4 '把StrB变量的指针(字符串的实际内存地址),赋值给StrA变量,作为字符串的指针
- CopyMemory ByVal VarPtr(StrB), pTmp, 4 '把临时变量记录的StrA的内存地址,赋值给StrB变量,作为字符串的指针
- End Sub
复制代码 理解VB中的指针,可以参考下面的文章
http://www.cnblogs.com/xw885/articles/105264.html
http://blog.csdn.net/slowgrace/article/details/4549926
顺便说一下,与BSTR类似,我们在VB中定义一个数组,其实定义的是COM里的SafeAraay结构指构的指针- Private Type SAFEARRAY
- cDims As Integer ''这个数组有几维?
- fFeatures As Integer ''这个数组有什么特性?
- cbElements As Long ''数组的每个元素有多大?
- cLocks As Long ''这个数组被锁定过几次?
- pvData As Long ''这个数组里的数据放在什么地方?
- ''rgsabound() As SFArrayBOUND
- End Type
复制代码
在VB中使用指针,可能涉及的函数包括:VarPtr、StrPtr、ObjPtr、VarPtrArray 和 VarPtrStringArray 函数
具体不在本贴讲解
|
|