Sub SwapStr(sA As String, sB As String)
Dim sTmp As String
sTmp = sA: sA = sB: sB = sTmp
End Sub
【程式二】:
& #39;用指針的做法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,程式二基本上要比程式一快一倍。下面是兩個函數在編譯成本機代碼後,運行不同次數所花時間的比較:
[entry("VarPtr"), hidden]
long _stdcall VarPtr([in] void* Ptr);
[entry("VarPtr"), hidden]
long _stdcall StrPtr([in] BSTR Ptr);
[entry("VarPtr"), hidden]
long _stdcall ObjPtr([in] IUnknown* Ptr);
即然它們是VB運行時庫中的同一個函數,我們也可以在VB裡用API方式重新聲明這幾個函數,如下:
Private Declare Function ObjPtr Lib "MSVBVM60" Alias "VarPtr" (var As Object) As Long
Private Declare Function VarPtr Lib "MSVBVM60" (var As Any) As Long
& #39;看看我們的東西被拷貝到哪兒去了
Sub TestCopyMemory()
Dim i As Long, k As Long
k = 5
i = VarPtr(k)
NOTE4: CopyMemory i, 40000, 4
Debug.Print k
Debug.Print i
i = VarPtr(k)
NOTE5: CopyMemory ByVal i, 40000, 4
Debug.Print k
End Sub
Public Function Compare(elem1 As Long, elem2 As Long) As Long
& #39;
End Function
Function FnPtrToLong(ByVal lngFnPtr As Long) As Long
FnPtrToLong = lngFnPtr
End Function
Sub PtrDemo()
Dim l As Long, c As Byte, ca() As Byte, Pt As POINT
Dim pl As Long, pc As Long, pv As Long, pPt As Long, pfnCompare As Long
c = AscB("X")
pl = VarPtr(l) '對應C裡的long、int型指針
pc = VarPtr(c) '對應char、short型指針
pPt = VarPtr(Pt) '結構指標
pv = VarPtr(ca(0)) '位元組陣列指標,可對應任何類型,也就是void*
pfnCompare = FnPtrToLong(AddressOf Compare) '函數指標
CopyMemory c, ByVal pc, LenB(c) '用指針取值
CopyMemory ByVal pc, AscB("Y"), LenB(c) '用指針賦值
pc = pc + LenB(c) : pl = pl - LenB(l) '指標移動
End Sub
& #39;使用更安全的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時,編譯器會報錯,比如現在我們用下面的語句:
【程式六】:
& #39;有點象【程式四】,但將常量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已經如此的流行,以至很多大師們也用它。它唯一的魅力就是不象用Long型指標那樣,需要我們自己調用VarPtr來得到指標,所有處理指標的工作由VB編譯器來完成。所以在參數的處理上,只用一條彙編指令:push [ i ],而用VarPtr時,由於需要函式呼叫,因此要多用五條彙編指令。五條多餘的彙編指令有時的確能我們冒著風險去用Any。
VB開發小組提供Any,就是想用ByRef xxx As Any來表達void* xxx。我們也完全可以使用VarPtr和Long型的指標來處理。我想,VB開發小組也曾猶豫過是公佈VarPtr,還是提供Any,最後他們決定還是提供Any,而繼續隱瞞VarPtr。的確,這是個兩難的決定。但是經過我上面的分析,我們應該知道,這個決定並不符合VB所追求的"更安全"的初衷。因為它可能會隱藏類型不符的錯誤,調試和找到這種運行時才產生的錯誤將花貴更多的時間和精力。
dim ab() As Byte , ret As long
& #39;傳遞Null值API會返回它所需要的緩衝區的長度。
ret = SomeApiNeedsBuffer(vbNullString)
& #39;動態分配足夠大小的記憶體緩衝區
ReDim ab(ret) As Byte
& #39;再次把指標傳給API,此時傳位元組陣列第一個元素的指標。
SomeApiNeedsBuffer(ByVal VarPtr(ab(1)))
& #39;標準的方法,也是高效的方法,但不容易理解。
Function LoWord(ByVal dw As Long) As Integer
If dw And &H8000& Then
LoWord = dw Or &HFFFF0000
Else
LoWord = dw And &HFFFF&
End If
End Function
【程式八】
& #39;用指標來做效率雖不高,但思想清楚。
Function LoWord(ByVal dw As Long) As Integer
CopyMemory ByVal VarPtr(LoWord), ByVal VarPtr(dw), 2
End Function
& #39;標準的移動陣列的做法
Private Sub ShitArray(ab() As MyType)
Dim i As Long, n As Long
n = CLng(UBound(ab) / 2)
For i = 1 To n
Value(n + i) = Value(i)
Value(i).data = 0
Next
End Sub
【程式十】:
& #39;用指針的做法
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(ByVal dest As Long, ByVal source As Long, ByVal bytes As Long)
Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" _
(ByVal dest As Long, ByVal numbytes As Long)
Private Declare Sub FillMemory Lib "kernel32" Alias "RtlFillMemory" _
(ByVal dest As Long, ByVal Length As Long, ByVal Fill As Byte)
Private Sub ShitArrayByPtr(ab() As MyTpye)
Dim n As Long
n = CLng(UBound(ab) / 2)
Dim nLenth As Long
nLenth = Len(Value(1))
'DebugBreak
CopyMemory ByVal VarPtr(Value(1 + n)), ByVal VarPtr(Value(1)), n * nLenth
ZeroMemory ByVal VarPtr(Value(1)), n * nLenth
End Sub
& #39;交換兩個字串最快的方法
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As Any, Source As Any, ByVal Length As Long)
Sub SwapStrPtr3(sA As String, sB As String)
Dim lTmp As Long
Dim pTmp As Long, psA As Long, psB As Long
pTmp = StrPtr(sA): psA = VarPtr(sA): psB = VarPtr(sB)
CopyMemory ByVal psA, ByVal psB, 4
CopyMemory ByVal psB, pTmp, 4
End Sub