查看: 507  |  回复: 0
  VB6 计算数据类型占用内存大小(占几个字节)隐藏函数 VarPtr StrPtr 看变量地址
楼主
发表于 2023年11月25日 18:00

需求背景

C、C++和.net语言都提供了计算变量内存大小的函数即SizeOf,该函数能正确返回数据类型占用的内存字节数,但是VBA、VB6.0没有直接提供。特别在调用Windows API的时候,该功能显示得特别重要。

实现思路

VB6.0虽然没有提供指针运算符,但提供了几个获取变量指针(内存地址)的值的函数。

新建From1(窗体),新建t(TextBox),Command1、Command2(按钮CommandButton),代码:

具体代码只能由VIP查看,请升级

运行结果:(&H是我后期添加的结论)

VarPtr(i(1))  = 142505232
VarPtr(i(2))  = 142505234
VarPtr(i(3))  = 142505236
VarPtr(i(4))  = 142505238
VarPtr(i(5))  = 142505240
----------
StrPtr(i(1))  = 142505180
StrPtr(i(2))  = 142505180
StrPtr(i(3))  = 142505180
StrPtr(i(4))  = 142505180
StrPtr(i(5))  = 142505180
----------
VarPtr(l(1))   = 377404000
VarPtr(l(2))   = 377404004
VarPtr(l(3))   = 377404008
VarPtr(l(4))   = 377404012
VarPtr(l(5))   = 377404016
----------
StrPtr(l(1))  = 142505180
StrPtr(l(2))  = 142505180
StrPtr(l(3))  = 142505180
StrPtr(l(4))  = 142505180
StrPtr(l(5))  = 142505180
----------
VarPtr(s(1))  = 377404192
VarPtr(s(2))  = 377404196
VarPtr(s(3))  = 377404200
VarPtr(s(4))  = 377404204
VarPtr(s(5))  = 377404208
----------
StrPtr(s(1))  = 379393388
StrPtr(s(2))  = 379393820
StrPtr(s(3))  = 379393892
StrPtr(s(4))  = 142505516
StrPtr(s(5))  = 142505068
----------
VarPtr(s1)  = 1700212
VarPtr(s2)  = 1700196
VarPtr(s3)  = 1700180
VarPtr(s4)  = 1700164
VarPtr(s5)  = 1700160
----------
StrPtr(s1)  = 146467388 &H8BAEA3C
StrPtr(s2)  = 379394324 &H169D1914
StrPtr(s3)  = 379366460 &H169CAC3C
StrPtr(s4)  = 142506020 &H87E7824
StrPtr(s5)  = 142504676 &H87E72E4
----------
__________________________________________________________________________________________
Start Address = &H8BAEA3C	 Number of Bytes = 32

Address:  OS:   Memory:                                        ASCII:
08BAEA3C  0000  77 00 77 00 77 00 2E 00 6D 00 61 00 6E 00 6F   w.w.w...m.a.n.o.
08BAEA4C  0010  6E 00 67 00 6B 00 75 00 2E 00 63 00 6F 00 6D   n.g.k.u...c.o.m.
__________________________________________________________________________________________
__________________________________________________________________________________________
Start Address = &H169D1914	 Number of Bytes = 32

Address:  OS:   Memory:                                        ASCII:
169D1914  0000  47 00 6F 00 6F 00 64 00 21 00 1F 77 84 76 7D   G.o.o.d.!..w.v}Y
169D1924  0010  E6 54 2E 00 00 00 00 00 00 00 00 00 57 81 02   .T..........W...
__________________________________________________________________________________________
__________________________________________________________________________________________
Start Address = &H169CAC3C	 Number of Bytes = 32

Address:  OS:   Memory:                                        ASCII:
169CAC3C  0000  59 00 65 00 73 00 2E 00 00 00 44 00 4C 00 4C   Y.e.s.....D.L.L.
169CAC4C  0010  00 00 00 00 06 00 00 00 08 00 00 00 3F 34 03   ............?4..
__________________________________________________________________________________________
__________________________________________________________________________________________
Start Address = &H87E7824	 Number of Bytes = 32

Address:  OS:   Memory:                                        ASCII:
087E7824  0000  4F 00 6B 00 61 00 79 00 2E 00 00 00 57 E0 E1   O.k.a.y.....W...
087E7834  0010  57 E0 E1 E4 5E E9 8F EC E4 43 72 47 F9 AB 23   W...^....CrG..#G
__________________________________________________________________________________________
__________________________________________________________________________________________
Start Address = &H87E72E4	 Number of Bytes = 32

Address:  OS:   Memory:                                        ASCII:
087E72E4  0000  48 00 69 00 2E 00 00 00 00 00 00 00 97 EA E1   H.i.............
087E72F4  0010  97 EA E1 E4 9E E3 8F EC 24 49 72 47 ED E7 23   ........$IrG..#G
__________________________________________________________________________________________

其它数据类型都可以通过这样的方式来计算出内存的大小,这样调用Windows API 就非常方便了。

1楼
发表于 2023年11月25日 22:29

关于 77 00 ,参考 《VB6 串口通信,发送正常,接收的数据总是不对,数据中多了几个 00

上方如果

s1 = "www.manongku.com"

变成了

s1 = "我www.manongku.com"

那么汉字在内存中就是就是:

11 62 77 00 77 00 77 00 2E 00...

关于 62 11 = 我,参考《VB6 将Unicode转中文

看来Intel系列采用小端序,把低位的11放在低端,把高位的62放到高端。

2楼
发表于 2023年12月1日 15:05

深入一下:

如果我们在代码

     '后续结果随机
    t.Text = t.Text & "StrPtr(s1)  = " & StrPtr(s1) & vbCrLf
    t.Text = t.Text & "StrPtr(s2)  = " & StrPtr(s2) & vbCrLf
    t.Text = t.Text & "StrPtr(s3)  = " & StrPtr(s3) & vbCrLf
    t.Text = t.Text & "StrPtr(s4)  = " & StrPtr(s4) & vbCrLf
    t.Text = t.Text & "StrPtr(s5)  = " & StrPtr(s5) & vbCrLf
    t.Text = t.Text & strLine

后修改s1、s2从而变成:

     '后续结果随机
    t.Text = t.Text & "StrPtr(s1)  = " & StrPtr(s1) & vbCrLf
    t.Text = t.Text & "StrPtr(s2)  = " & StrPtr(s2) & vbCrLf
    t.Text = t.Text & "StrPtr(s3)  = " & StrPtr(s3) & vbCrLf
    t.Text = t.Text & "StrPtr(s4)  = " & StrPtr(s4) & vbCrLf
    t.Text = t.Text & "StrPtr(s5)  = " & StrPtr(s5) & vbCrLf
    t.Text = t.Text & strLine
    
    s1 = "你好www.manongku.com"
    s2 = "Hiwww.qq.com"
     '后续结果随机2
    t.Text = t.Text & "StrPtr(s1)  = " & StrPtr(s1) & vbCrLf
    t.Text = t.Text & "StrPtr(s2)  = " & StrPtr(s2) & vbCrLf
    t.Text = t.Text & "StrPtr(s3)  = " & StrPtr(s3) & vbCrLf
    t.Text = t.Text & "StrPtr(s4)  = " & StrPtr(s4) & vbCrLf
    t.Text = t.Text & "StrPtr(s5)  = " & StrPtr(s5) & vbCrLf
    t.Text = t.Text & strLine

可以看到运行结果修改了的s1、s2变了地址,而没修改的s3、s4、s5地址不变。看来是CPU重新申请了地址。

StrPtr(s1)  = 192503724
StrPtr(s2)  = 63122836
StrPtr(s3)  = 63122276
StrPtr(s4)  = 191219644
StrPtr(s5)  = 191219668
----------
StrPtr(s1)  = 192506316
StrPtr(s2)  = 63122476
StrPtr(s3)  = 63122276
StrPtr(s4)  = 191219644
StrPtr(s5)  = 191219668
----------

显示试试数组:

    s(1) = "www.manongku.com"
    s(2) = "Good!真的好哦."
    s(3) = "Yes."
    s(4) = "Okay."
    s(5) = "Hi."

    t.Text = ""
    
    t.Text = t.Text & StrPtr(s(1)) & vbCrLf
    t.Text = t.Text & StrPtr(s(2)) & vbCrLf
    t.Text = t.Text & StrPtr(s(3)) & vbCrLf
    t.Text = t.Text & StrPtr(s(4)) & vbCrLf
    t.Text = t.Text & StrPtr(s(5)) & vbCrLf & vbCrLf
    
    s(1) = "你好www.manongku.com"
    s(2) = "HiGood!真的好哦."
    
    t.Text = t.Text & StrPtr(s(1)) & vbCrLf
    t.Text = t.Text & StrPtr(s(2)) & vbCrLf
    t.Text = t.Text & StrPtr(s(3)) & vbCrLf
    t.Text = t.Text & StrPtr(s(4)) & vbCrLf
    t.Text = t.Text & StrPtr(s(5)) & vbCrLf

运行结果也一样,修改了的字符串地址会变动,没修改的字符串不会变动。代码如下:

    s(1) = "www.manongku.com"
    s(2) = "Good!真的好哦."
    s(3) = "Yes."
    s(4) = "Okay."
    s(5) = "Hi."

    t.Text = ""
    
    t.Text = t.Text & StrPtr(s(1)) & vbCrLf
    t.Text = t.Text & StrPtr(s(2)) & vbCrLf
    t.Text = t.Text & StrPtr(s(3)) & vbCrLf
    t.Text = t.Text & StrPtr(s(4)) & vbCrLf
    t.Text = t.Text & StrPtr(s(5)) & vbCrLf & vbCrLf
    
    s(1) = "你好www.manongku.com"
    s(2) = "HiGood!真的好哦."
    
    t.Text = t.Text & StrPtr(s(1)) & vbCrLf
    t.Text = t.Text & StrPtr(s(2)) & vbCrLf
    t.Text = t.Text & StrPtr(s(3)) & vbCrLf
    t.Text = t.Text & StrPtr(s(4)) & vbCrLf
    t.Text = t.Text & StrPtr(s(5)) & vbCrLf

运行结果:

192499908
63992044
63992724
63991604
63992884

192504084
192902316
63992724
63991604
63992884

现在看下定长字符串:

如果把

Dim s(1 To 5) As String

改成

Dim s(1 To 5) As String * 100

那么运行结果就是不变:

192607964
192607964
192607964
192607964
192607964

192607964
192607964
192607964
192607964
192607964

对于数组肯定也是不变的,因为本身就是定长,代码如下:

    t.Text = ""
    
    t.Text = t.Text & VarPtr(i(1)) & vbCrLf
    t.Text = t.Text & VarPtr(i(2)) & vbCrLf
    t.Text = t.Text & VarPtr(i(3)) & vbCrLf
    t.Text = t.Text & VarPtr(i(4)) & vbCrLf
    t.Text = t.Text & VarPtr(i(5)) & vbCrLf & vbCrLf
    
    i(1) = 6
    i(2) = 7
    i(3) = 3
    i(4) = 4
    i(5) = 5
    
    t.Text = t.Text & VarPtr(i(1)) & vbCrLf
    t.Text = t.Text & VarPtr(i(2)) & vbCrLf
    t.Text = t.Text & VarPtr(i(3)) & vbCrLf
    t.Text = t.Text & VarPtr(i(4)) & vbCrLf
    t.Text = t.Text & VarPtr(i(5)) & vbCrLf

运行结果:

64083368
64083370
64083372
64083374
64083376

64083368
64083370
64083372
64083374
64083376

关于字符串数组占用内存的测试报告,请看《VB6 如何节约内存?Dim定义到过程当局部变量与全局的区别》。

3楼
发表于 2023年12月8日 19:16

对于变量,看下它在不同子程序中是否被复制?

Private Sub Command1_Click()
    Dim arr(0) As Byte
    arr(0) = 65
    Debug.Print VarPtr(0)
    Call s_1(arr)
End Sub

Private Sub s_1(arr() As Byte)
    Debug.Print VarPtr(0)
    Call s_2(arr)
End Sub

Private Sub s_2(arr() As Byte)
    Debug.Print VarPtr(0)
End Sub

运行结果:

 1700298 
 1700142 
 1699986

证明了,再次调用,测试局部变量或全局变量地址也会变,这样效率低。能节省s_1就不用s_1最快。

4楼
发表于 2023年12月19日 20:23
Private Sub Command1_Click()
    Dim bArr() As Byte

    ReDim bArr(10)
    Debug.Print VarPtr(bArr(0))
    
    ReDim Preserve bArr(10)
    Debug.Print VarPtr(bArr(0))
    
     ReDim bArr(20)
    Debug.Print VarPtr(bArr(0))
    
    ReDim Preserve bArr(20)
    Debug.Print VarPtr(bArr(0))
End Sub

运行结果:

 197909000 
 197909000 
 63627392 
 63627392


5楼
发表于 2024年1月2日 15:54

看代码:

Private Sub Command1_Click()
    Dim s1 As String
    
    s1 = "www.manongku.com"
    Debug.Print StrPtr(s1)
    
    s1 = Left$(s1, 1)
    Debug.Print StrPtr(s1)
    
    s1 = "码农库"
    Debug.Print StrPtr(s1)
End Sub

运行结果:

 198742284 
 197567756 
 197570516

如果字符串缩小,地址也会变,不存在直接给字符串截取后加0而节约的情况。增加也会变。

您需要登录后才可以回帖 登录 | 立即注册
【本版规则】请勿发表违反国家法律的内容,否则会被冻结账号和删贴。
用户名: 立即注册
密码:
2020-2024 MaNongKu.com