|
可恶的VB 快乐的.NET。 由于Windows操作系统的系统功能大部分是以API函数的形式提供给用户,而API函数的最初编写基本上都是以C语言实现的,由此一来,C语言的指针操作遍布Windows编程的方方面面,以前的VB程序员低人一等原因很简单:VB所有的版本(不包括VB.net)都不支持完整的指针操作,所以根本无法调用一些Wincows的核心功能!微软既不能完全将API实现的功能封装到控件库或类库中,又不允许VB程序员使用指针以调用!所以当时很多用VC可以实现的程序VB程序员就实现不了并不是水平问题,而是微软的产品方针在作怪。那年头VB唯一的优势就是可视化界面编程,以大量的控件实现一拖即用的效果,但时至今日,C#的可视化编程和VB差不多没什么两样,界面编写的优势一去不复返,根据VB6留下来的没法进行底层处理的烂名声,一大批的VB程序员转投C#阵营。综上所述,要完全发挥windows的能量,首先必须掌握指针这一武器,我们VB程序员需要指针这把利剑,以刺穿老板的心脏,让他们以与C#程序员同等的待遇对待我们。
一、.net指针的定义
1、内存地址
由于可能看本文的VB读者或许没有接触过指针的概念,所我在此对指针的概念定义简单作一阐述。
计算机的数据处理是基于内存地址进行的,所谓内存地址就是将内存可以存储数据的空间人为地按字节顺序进行数字编号,当程序对某一变量进行存取时计算机就根据变量的地址以确定变量中实际存储的数据内容。表1 是一个假想的内存地址示意图,我们可以看到,假设计算机将字节数组a实际分配到2000开始的连续三个地址空间后,对数组的赋值就会在这三个字节的内存空间中进行。另一个变量b由于是一个32位的整数,所以它占用了2003、2004两个地址的空间,在3070中放了一个byte类型的c变量
表1:假想的内存地址示意图
内存地址
0
1
……
2000
2001
2002
2003
2004
……
3070
……
最高编号取决于内存的容量
变量实际数据
系统保留
系统保留
a
b
c
70
0
变量声明
dim a(2) as byte={a,b,c}
dim b as int32=70
dim c as byte
2、指针的定义和作用
1)指针是一特殊的变量,它存储的就是一个内存地址编号,当某一个地址编号存储在指针变量中时,我们称指针指向以这个地址开始的数据。由于现在使用的win98\win2000\winxp\2003等操作系统均为32位系统,所以都是采用一个32位的整数记录内存地址(可表示的范围就是0~2的31次方),所以以上系统的指针存储的实际数据都是一个32位的整数。(但以后的64位Windows系统会以一个64位的整数记录内存地址,到时指针存储的将是一个64位的整数)。
指针到底有什么作用呢,在汇编语言及C语言的黑暗时代(很久很久以前的事了……),使用指针存储一个地址,然后根据这个地址进行数据的存储是获得高效率程序的最好途径,(一来,计算机内部是直接使用地址作数据存储的,计算机并不明白在高级语言中定义如A,B这样的变量名;二来地址的传送很方便,如函数的参数是以堆栈的方式传送的,以一个32或16位(Dos系统是16位的)的整数表示的数组地址入栈处理比将整个数组的内存入栈高效得多),但现在我们并不需要使用这种底级的程序优化方法,我们学习旨针的宗旨应该是:因为API是用C编写的,所以我们必须找到一个兼容使用这些函数的途径。
2)在net中的指针是没有指向类型之分的,所谓指向类型就是指针地址代表的实际数据类型(这是针对C语言而言的,在C语言中,如果一个指针的指向类型为char型,那么这个指针的指向的数据就是指针指向的地址值及其后一连续编号的地址所代表的内存区间中储存的16数据)。也就是说,net指针存储的是一个地址,而这个地址表示的数据类型可以理解为以这个地址开始的内存空间所能表示的各类数据。如表1中,假设我们使用一个储存着地址2000的指针p,我们可以理解p指向一个字节类型,其值就是a;也可以理解p指向一个3位字节的数组,其值是一个连续的a,b,c。
二、指针的使用
在本文的以下部分,我们假定程序已包括以下语句以引用net中处理指针的命名空间:
Imports System.Runtime.InteropServices
1、指针的申请
在net中,指针就是一个intptr的变量,由于指针存储的是一个32位整数地址的事实,我们可以直接将一个内存范围内的整数赋值给一个指针(使用intptr的构造函数,然后起直接将内存地址作为参数传入,例:dim pa as new intptr(777) ’这样就定义了一个指针,他指向内存地址以777开始的区域),然后就可以使用指针对这个地址中储存的数据进行读取或写入操作。但这种做法是危险的,地址分配是系统自动进行的,我们没法假定某一时刻在某一内存位置储存着什么数据,好象上面777编号的地址中存储了什么?这是系统的保留区域,你如想向这个地址读写数据,程序出错那是铁定的事了!除非你真正知道那个地址中到底放有什么数据,否则绝不要这样做。
正确的做法:首先向系统申请一块足够大的内存空间,然后保存这个内存空间的首地址到一个指针中。由于前面介绍过,net指针没有数据类型之分,所以假若我们想申请有某一指向类型的指针,只要申请一个这个数据类型有同等内存占用长度的字节数组或VB.net变量,然后将这个数组或变量的首地址存储在指针中就算申请成功了。表2是一些内存空间占用长度相等的windows标准数据类型和Vb.net数据类型的对照表
表2:windows标准数据类型和Vb.net数据类型的对照表
windows标准数据类型
vb.net数据类型
应该申请的字节数组
空间占用长度(以字节为单位)
Byte
Byte
1
CHAR
Byte
1
WChar
Char
Dim byt(1) as byte
2
Short
Int16
Dim byt(1) as byte
2
INT/INT32/LONG/UINT
Int32
Dim byt(3) as byte
4
DWORD
Int32
Dim byt(3) as byte
4
Bool
Int32
Dim byt(3) as byte
4
INT64/LONG64/ULONG64
Int64
Dim byt(7) as byte
8 例1:以下程序我们申请几个指向不同类型的指针:
’使用<StructLayout(LayoutKind.Sequential)>属性告诉net编译器:结构的元素在内存中按其出现的顺序排列 <StructLayout(LayoutKind.Sequential)> _ Public Structure DEFUDT_Test Public bytb As Byte Public i32a As Int32 End Structure
Public Function fnGetIntptr1() As IntPtr ’取得一个4字节数组指针 Dim tabytTest(3) As Byte ’以下语句告诉net垃圾回收进程不对tabytTest进行处理,也就是说tabytTest占用的内存区域固定不变。 Dim thObject As GCHandle = GCHandle.Alloc(tabytTest, GCHandleType.Pinned) Dim tpObject As IntPtr = thObject.AddrOfPinnedObject() ’取得指向字节数组的指针
’取得一个指向32位内存数据的指针, ’由于使用gchandle取指针的方法只能对引用的对象有效, ’所以对如int32等值类型必须使用将其封装
[1] [2] [3] 下一篇
|