分享

VB中的指针技术

 战神之家 2014-03-21
VB指针技术,如果只是想实现的话,很简单 
在VB中实现指针需要用到两个东西 

第一是VB内部的指针函数:varptr 
语法: varptr(Ptr as any) as long 

第二是API函数RtlMoveMemory 
定义方法 Public Declare Sub RtlMoveMemory Lib "kernel32"(Dest as any, Src as any, Length as long) 
当然,这里为了方便,你可以用Alias将RtlMoveMemory改成自己习惯的名字,比如你习惯于 
c的api封装语句那么就这样申明 
Public Declare Sub Memcpy Alias "RtlMoveMemory" ..........后面相同 
这个API的功能是:从Src处所在的内存中复制Length个字节到Dest所在的内存中,它不会管 
你有没有读写内存的权限,如果你超越了这个权限,你会得到一个Access Violation Fault。这里的Src和Dest可以是任何类型,因为它对应一个DLL里的无类型指针。 

准备完了就是指针的实现,在vb里实现指针的技术并不复杂,但功能也比较有限,由于需要 
借助API的支持,决定了它对于指针的操作不可能像C里那么随心所欲 

指针说白了就是一个内存地址,它是一个32位2进制数,所以定义一个指针,只要定义一个长整型变量就可以了 
Dim pVar as Long ' 这是一个指针 

然后我们来看vb能怎么用指针 
1. 取得指针 
pVar = varptr(var) 
原理:varptr函数给出参量var的内存地址,这样指针变量pVar中就会存放var的内存地址 

2. 取得一个指针所指的值,并存放到一个变量中 
Call Memcpy(ByRef var, ByVal pVar, LenB(var)) 
原理:和上例不同,这里的ByRef和ByVal写反了,它的解释是: 
   从pVar的值所指向的内存地址处复制LenB(var)个字节写到var这个变量所在的内存地址 
   由于这里var用了传址,被写的内存是var所占用的内存空间,这就变相的成了一个赋值语句,所赋的值是pVar这个指针指向地址的值。 

3. 指针移动 
pVar = pVar + LenB(var): pVar = pVar - LenB(var) 
原理:很好理解,数据在内存中是动态连续存放的,通过测量var的长度可以跳到下一个存储单元或前一个存储单元。 

vb的指针操作就是以上3个,当然,如果你足够疯狂,可以通过类的封装来实现你所熟悉的c 格式的指针语法。 

比较枯燥,但为了应用铺垫,我尽可能简单的说一下 
首先是关于Byval和Byref, 
vb内部不是没有指针(否则它怎么访问内存),它只是对一般用户屏蔽了指针的操作而已 
在vb的函数调用中,Byval和Byref就是通过不同的指针应用来实现的。 
首先你可以测试下面这个 
dim a as long 
  a = 32769 
  debug.print varptr(a) 
  debug.print varptr(byval a) 
  debug.print varptr(32769) 
  debug.print varptr(byval 32769) 
  运行结果,第三个结果是第一个结果-4,而第二个结果和第四个结果都是32769 

原因如下: 
当你用byref调用一个参数时,它将参数变量或常量的内存地址传给过程 
当你用byval调用一个参数时,它将参数变量或常量本身传给过程 
这两句话当然初学vb的也知道,我解释一下它们在varptr里的原理 
  varptr用来取得参数或变量的地址,因为没有在MSDN中公开,显得好像很神秘,实际上它是一个很笨的函数:你传给它什么值,它就返回给你什么值。 
  当我们用默认的byref传递一个参数给varptr时,它接受到的是参数的内存地址,于是它会返回这个内存地址。 
  当我们用显式指定byval传递一个参数给varptr时,它接受到的就是这个值,于是它就返回这个值,这就说明了为什么varptr(byval a)会返回a的值的原因。 
  不难看出,向过程或函数传递参数时,Byref a等同于Byval varptr(a),这大概也就是两者工作方式的不同吧。 

说完这个以后就再来说一下Memcpy,也就是API RtlMoveMemory 

RtlMoveMemory在c里的定义中,前两个参数是void*,也就是无类型指针,后一个是长整型数,由于在vb里我们没法声明void*这种东西,于是只能放弃类型声明为any。 
事实上前面说过,指针就是一个长整型数据,因此void*也不例外,它也是接受一个长整型数据,只不过API会解释成它是一个指向内存某个地方的指针罢了。 
知道了这一点,就意味着我们在调用Memcpy时使用byref和byval的不同会导致结果的完全不 
同。 

看下例: 
dim k as long, i as long, t as long 
  i = 43092 
  k = varptr(i) 
  t = 32767 
  Call Memcpy(k, t, 4) 
  Call Memcpy(Byval k, t, 4) 
  Call Memcpy(k, Byval t, 4) 
  Call Memcpy(Byval k, Byval t, 4) 
  Call Memcpy(varptr(k), t, 4) 
  Call Memcpy(Byval varptr(k), t, 4) 
  Call Memcpy(k, Byval varptr(t), 4) 
  Call Memcpy(byval k, varptr(t), 4) 

  第一句:从存放变量t的内存地址处,拷贝4个字节到存放变量k的内存地址处。说的这么绕口,其结果就相当于k占用的那4个字节中的值变成了t的数值,即等于赋值语句k = t 
  第二句:从存放t这个值的内存地址处,拷贝4个字节到变量k的值所代表的内存地址处。由于变量k的值所代表的值是i的指针,因此执行后,i的值变成了t的值。 
  第三句:从内存地址t=32767处拷贝4个字节到存放变量k的内存地址处。由于32767这个地址我们没有权利访问,这一句执行会得到一个Access Violation Fault: 试图访问内存0x00007FFF时出错,该内存不能为'Read'。 
  第四句:出错,原因同前。 
  第五句:[byref] varptr(k)是什么意思?从存放变量t的内存地址处,拷贝4个字节到.....到哪儿呢?到存放着"k的指针"这个值的内存地址处。其结果是存放着"k的指针"的地址处值变成了32767,但由于我们这里没有用任何变量来保存varptr(k)这个值,因此随着api调用完毕的堆栈释放,这个地址已经失效了,因此这个Memcpy什么也没有做。 
  第六句:前面解释过了,Byval varptr(k)就等同于Byref k,所以同第一句,将k的值变成了65535。 
  第七句:前面解释过了,同第一、六句 
  第八句:我先不说,看你们能不能判断出来这是什么……………………该句执行以后,变量i的值将会变为表示t的指针的数值,想明白了吗? 

说了这么多,总结一下就是: 
vb指针的工作原理: 
1. 笨笨的varptr函数:a = varptr(byval a)  
2. 传值与传址的转换:byref a 相当于 byval varptr(a) 
3. Memcpy的几种工作方式,调用时注意避开3、4、5,否则你会得不到你要的效果 


发信人: sallydyw (Iris|flashcat), 信区: VisualBasic 
标  题: VB的指针技术实现方法 
发信站: 水木社区 (Thu Dec 22 01:42:00 2005), 转信 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多