设为首页收藏本站Access中国

Office中国论坛/Access中国论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

返回列表 发新帖
查看: 1932|回复: 6
打印 上一主题 下一主题

[Access本身] 【转载 / 文章】真没想到VB也可以这样用之指针技术

[复制链接]
跳转到指定楼层
1#
发表于 2005-8-14 19:31:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
真没想到VB也可以这样用之指针技术

作者:adambear 更新时间: 2005-05-03     

  

   想当年东方不败,黑木崖密室一战,仅凭一根绣花针独战四大高手,神出鬼没,堪称天下武林第一高手。若想成为VB里的东方不败,熟习VB《葵花宝典》,掌握VB指针技术,乃是不二的法门。

   欲练神功,引刀……,其实掌握VB指针技术,并不需要那么痛苦。因为说穿了,也就那么几招,再勤加练习,终可至神出鬼没之境。废话少说,让我们先从指针的定义说起。

一、指针是什么?

   不需要去找什么标准的定义,它就是一个32位整数,在C语言和在VB里都可以用Long类型来表示。在32位Windows平台下它和普通的32位长整型数没有什么不同,只不过它的值是一个内存地址,正是因为这个整数象针一样指向一个内存地址,所以就有了指针的概念。

   有统计表明,很大一部分程序缺陷和内存的错误访问有关。正是因为指针直接和内存打交道,所以指针一直以来被看成一个危险的东西。以至于不少语言,如著名的JAVA,都不提供对指针操作的支持,所有的内存访问方面的处理都由编译器来完成。而象C和C++,指针的使用则是基本功,指针给了程序员极大的自由去随心所欲地处理内存访问,很多非常巧妙的东西都要依靠指针技术来完成。

   关于一门高级的程序设计语言是不是应该取消指针操作,关于没有指针操作算不算一门语言的优点,我在这里不讨论,因为互联网上关于这方面的没有结果的讨论,已经造成了占用几个GB的资源。无论最终你是不是要下定决心修习指针技术《葵花宝典》,了解这门功夫总是有益处的。

   注意:在VB里,官方是不鼓励使用什么指针的,本文所讲的任何东西你都别指望取得官方的技术支持,一切都要靠我们自己的努力,一切都更刺激!

   让我们开始神奇的VB指针探险吧!

二、来看看指针能做什么?有什么用?

   先来看两个程序,程序的功能都是交换两个字串:

   【程序一】:

'标准的做法SwapStr

Sub SwapStr(sA As String, sB As String)

  Dim sTmp As String

  sTmp = sA: sA = sB: sB = sTmp

End Sub  

   【程序二】:

'用指针的做法SwapPtr

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

Sub SwapPtr(sA As String, sB As String)

  Dim lTmp As Long

  CopyMemory lTmp, ByVal VarPtr(sA), 4

  CopyMemory ByVal VarPtr(sA), ByVal VarPtr(sB), 4

  CopyMemory ByVal VarPtr(sB), lTmp, 4

End Sub  

   你是不是以为第一个程序要快,因为它看着简单而且不用调用API(调用API需要额外的处理,VB文档明确指出大量调用API将降低程序性能)。但事实上,在VB集成环境中运行,程序二要比程序一快四分之一;而编译成本机代码或p-code,程序二基本上要比程序一快一倍。下面是两个函数在编译成本机代码后,运行不同次数所花时间的比较:

   运行100000次,SwapStr需要170毫秒,SwapPtr需要90毫秒。

   运行200000次,SwapStr需要340毫秒,SwapPtr需要170毫秒。

   运行2000000次,SwapStr需要3300毫秒,SwapPtr需要1500毫秒。

   的确,调用API是需要额外指令来处理,但是由于使用了指针技术,它没有进行临时字串的分配和拷贝,因此速度提高了不少。

   怎么样,想不到吧!C/C++程序员那么依赖指针,无非也是因为使用指针往往能更直接的去处理问题的根源,更有驾驭一切的快感。他们不是不知道使用指针的危险,他们不是不愿意开卫星定位无级变速的汽车,只是骑摩托更有快感,而有些地方只有摩托才走得过去。

和在C里类似,在VB里我们使用指针也不过三个理由:

   一是效率,这是一种态度一种追求,在VB里也一样;

   二是不能不用,因为操作系统是C写的,它时刻都在提醒我们它需要指针;

   三是突破限制,VB想照料我们的一切,VB给了我们很强的类型检查,VB象我们老妈一样,对我们关心到有时我们会受不了,想偶尔不听妈妈的话吗?你需要指针!

   但由于缺少官方的技术支持,在VB里,指针变得很神秘。因此在C里一些基本的技术,在VB里就变得比较困难。本文的目的就是要提供给大家一种简单的方法,来将C处理指针的技术拿到VB里来,并告诉你什么是可行的,什么可行但必须要小心的,什么是可能但不可行的,什么是根本就不可能的。

三、 程咬金的三板斧

   是的,程序二基本上就已经让我们看到VB指针技术的模样了。总结一下,在VB里用指针技术我们需要掌握三样东西:CopyMemory,VarPtr/StrPtr/ObjPtr, AdressOf. 三把斧头,程咬金的三板斧,在VB里Hack的工具。

   1、CopyMemory

   关于CopyMemory和Bruce McKinney大师的传奇,MSDN的Knowledge Base中就有文章介绍,你可以搜索"ID: Q129947"的文章。正是
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 分享淘帖 订阅订阅
2#
 楼主| 发表于 2005-8-14 19:31:00 | 只看该作者
四、指针使用中应注意的问题

   1、关于ANY的问题

   如果以一个老师的身份来说话,我会说:最好永远也不要用Any!是的,我没说错,是永远!所以我没有把它放在程咬金的三板斧里。当然,这个问题和是不是应该使用指针这个问题一样会引发一场没有结果的讨论,我告诉你的只是一个观点,因为有时我们会为了效率上的一点点提高或想偷一点点懒而去用Any,但这样做需要要承担风险。

   Any不是一个真正的类型,它只是告诉VB编译器放弃对参数类型的检查,这样,理论上,我们可以将任何类型传递给API。

   Any在什么地方用呢?让我们来看看,在VB文档里的是怎么说的,现在就请打开MSDN(Visual Studio 6自带的版本),翻到"Visual Basic文档"->"使用Visual Basic"->"部件工具指南"->"访问DLL和Windows API"部分,再看看"将 C 语言声明转换为 Visual Basic 声明"这一节。文档里告诉我们,只有C的声明为LPVOID和NULL时,我们才用Any。实际上如果你愿意承担风险,所有的类型你都可以用Any。当然,也可以如我所说,永远不要用Any。

   为什么要这样?那为什么VB官方还要提供Any?是信我的,还是信VB官方的?有什么道理不用Any?

   如前面所说,VB官方不鼓励我们使用指针。因为VB所标榜的优点之一,就是没有危险的指针操作,所以的内存访问都是受VB运行时库控制的。在这一点上,JAVA语言也有着同样的标榜。但是,同JAVA一样,VB要避免使用指针而得到更高的安全性,就必须要克服没有指针而带来的问题。VB已经尽最大的努力来使我们远离指针的同时拥有强类型检查带来的安全性。但是操作系统是C写的,里面到处都需要指针,有些指针是没有类型的,就是C程序员常说的可怕的void*无类型指针。它没有类型,因此它可以表示所有类型。如CopyMemory所对应的是C语言的memcpy,它的声明如下:

void *memcpy( void *dest, const void *src, size_t count );  

   因memcpy前两个参数用的是void*,因此任何类型的参数都可以传递给他。

   一个用C的程序员,应该知道在C函数库里这样的void*并不少见,也应该知道它有多危险。无论传递什么类型的变量指针给上面memcpy的void*,C编译器都不会报错或给任何警告。

   在VB里大多数时候,我们使用Any就是为了使用void*,和在C里一样,VB也不对Any进行类型检查,我们也可以传递任何类型给Any,VB编译器也都不会报错或给任何警告。

   但程序运行时会不会出错,就要看使用它时是不是小心了。正因为在C里很多错误是和void*相关的,所以,C++鼓励我们使用satic_cast<void*>来明确指出这种不安全的类型的转换,已利于发现错误。

   说了这么多C/C++,其实我是想告诉所有VB的程序员,在使用Any时,我们必须和C/C++程序员使用void*一样要高度小心。 

   VB里没有satic_cast这种东西,但我们可以在传递指针时明确的使用long类型,并且用VarPtr来取得参数的指针,这样至少已经明确地指出我们在使用危险的指针。如程序二经过这样的处理就成了下面的程序:

   【程序五】:

'使用更安全的CopyMemory,明确的使用指针!

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

Sub SwapStrPtr2(sA As String, sB As String)

  Dim lTmp As Long

  Dim pTmp As Long, psA As Long, psB As Long

  pTmp = VarPtr(lTmp): psA = VarPtr(sA): psB = VarPtr(sB)

  CopyMemory pTmp, psA, 4

  CopyMemory psA, psB, 4

  CopyMemory psB, pTmp, 4

End Sub  

   注意,上面CopyMemory的声明,用的是ByVal和long,要求传递的是32位的地址值,当我们将一个别的类型传递给这个API时,编译器会报错,比如现在我们用下面的语句:

   【程序六】:

'有点象【程序四】,但将常量40000换成了值为1的变量.

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

Sub TestCopyMemory()

  Dim i As Long,k As Long, z As Interger

  k = 5 : z = 1

  i = VarPtr(k)

  '下面的语句会引起类型不符的编译错误,这是好事!

  'CopyMemory i, z, 4

  '应该用下面的

  CopyMemory i, ByVal VarPtr(z), 2

  Debug.Print k

End Sub  

   编译会出错!是好事!这总比运行时不知道错在哪儿好!

   象程序四那样使用Any类型来声明CopyMemory的参数,VB虽然不会报错,但运行时结果却是错的。不信,你试试将程序四中的40000改为1,结果i的值不是我们想要的1,而是327681。为什么在程序四中,常量为1时结果会出错,而常量为40000时结果就不错?

   原因是VB对函数参数中的常量按Variant的方式处理。是1时,由于1小于Integer型的最大值32767,V
3#
 楼主| 发表于 2005-8-14 19:38:00 | 只看该作者
看完这篇文章,相信大家对于VB的理解可以更上一个台阶。
4#
 楼主| 发表于 2005-8-14 20:11:00 | 只看该作者
文章是AdamBear的VB真是想不到系列中的一章,AdamBear写的东西都很有深度,对他的作品感兴趣的可以看看他的作品集:http://www.csdn.net/develop/list_article.asp?author=AdamBear&myclassid=5900&myclassName=VB
5#
发表于 2005-8-16 20:50:00 | 只看该作者
收下,谢谢!!!!!
6#
发表于 2005-8-17 06:41:00 | 只看该作者
很受启发!
7#
发表于 2009-10-24 15:39:46 | 只看该作者
收藏了看看。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )  

GMT+8, 2024-6-3 05:02 , Processed in 0.097731 second(s), 30 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

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