Ogni tanto accade di dover scambiare tra loro il contenuto di due variabili. Sembra proprio che il nostro VBA non permetta lo scambio direttamente, ma che sia necessario passare attraverso una variabile di appoggio temporanea:
tmp = s1
s1 = s2
s2 = tmp
Questo piccolo spuntino di Excel, tratto da uno dei suggerimenti di Francesco Balena, ci mostra invece che basta una chiamata ad una API piuttosto comune, CopyMemory (RtlMoveMemory) che invece di allocare fisicamente spazio per una nuova variabile, spostarvi il contenuto della prima, quindi riassegnare i diversi valori, semplicemente scambia tra loro i puntatori ai descrittori di stringa in memoria… e lo scambio di coppia è fatto 🙂
L’API CopyMemory si dichiara così in testa a un modulo:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal numBytes As Long)
L’API CopyMemory copia un blocco di memoria da una locazione a un’altra quindi la velocità di esecuzione è quasi istantanea. Riceve tre parametri:
Dest: Un puntatore all’indirizzo iniziale del blocco memoria di destinazione
Source: Un puntatore all’indirizzo iniziale del blocco di memoria da copiare
numBytes: La dimensione del blocco di memoria da copiare, in bytes.
Ecco un codice completo da testare, la prima funzione esegue uno scambio classico, la seconda sfrutta CopyMemory e la sub di test mostra invece che i tempi di esecuzione sono ridotti almeno della metà quando sono in gioco stringhe enormi.
Option Explicit
' scambio si stringhe tra loro
' da I trucchi di VB6 di F. Balena
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(dest As Any, source As Any, ByVal numBytes As Long)
'metodo classico
Sub swap()
Dim tmp As String, s1 As String, s2 As String
Randomize Timer
s1 = String(99999999#, Chr(Int(Rnd * 26) + 65))
s2 = String(99999999#, Chr(Int(Rnd * 26) + 65))
tmp = s1
s1 = s2
s2 = tmp
End Sub
'metodo smart
Sub swap2()
Dim s1 As String
Dim s2 As String
Dim saveAddr As Long
Randomize Timer
s1 = String(99999999#, Chr(Int(Rnd * 26) + 65))
s2 = String(99999999#, Chr(Int(Rnd * 26) + 65))
' salva il descrittore della prima stringa
saveAddr = StrPtr(s1)
' copia il descrittore di s2 in quello di s1 (32 bit)
CopyMemory ByVal VarPtr(s1), ByVal VarPtr(s2), 4
' completa lo scambio dei descrittori
CopyMemory ByVal VarPtr(s2), saveAddr, 4
End Sub
Sub test_swap()
Dim t1 As Single, t2 As Single
Dim f1 As Single, f2 As Single
Dim s1 As String, s2 As String
t1 = Timer
swap
t2 = Timer
f1 = (t2 - t1) * 1000
t1 = Timer
swap2
t2 = Timer
f2 = (t2 - t1) * 1000
MsgBox "1° test tempo trascorso = " & _
Format(f1, "0.000 \m\s") & vbNewLine & _
"2° test tempo trascorso = " & _
Format(f2, "0.000 \m\s")
End Sub
› Scambiare contenuto di due variabili