查看: 456  |  回复: 0
  VB6 字符串探究 VB/VBA字符串,是个冒牌货,难怪那么慢!BSTR结构
楼主
发表于 2024年3月20日 20:00

其中VB/VBA的String类型,就是COM中的BSTR类型。将BSTR的定义翻译成VB/VBA,就是下面的样子:

Private Type BSTR    '标准BSTR结构
    dwSize As Long    '[4] 字符串数据字节长度前缀
    pData() As Integer    '[N] 实际数据
    wEnd As Integer    '[2] 结束符占2字
End Type

可见VB/VBA的String,既有前缀,也有结尾,因此也被称为安全字符串。这与C中的字符串有很大的不同,C依赖Null结束符来判断是否越界,在指针的加持下非常容易越界访问。VB/VBA中的String,无论如何使用都不存在越界的问题。


我们知道String类型其实是一个指针的指针,也即在字符串变量指针指向的内存里存储的是字符串数据指针。通过上述验证代码,可以发现声明1个非定长字符串变量时,仅仅是分配了1个指针所需内存,因此String变量的尺寸其实只有4Bytes。该指针未指向任何位置,也即此时的字符串变量其实是一个空指针。其实想想也是,你什么数据都没给,天知道要给第二个成员多大地儿。

StrPtr函数可以获取字符串数据指针,也即第二个成员的首地址。这个数据指针就存放在声明字符串变量时,分配的内存里,也就是说字符串变量的值是一个指针。这和我们通常动态监视的字符串变量,是不一样的。因为在IDE环境下,实际上给我们看的字符串,是已经转换后的。

字符串变量在被赋值之后,字符串变量里存放的确实是字符串数据指针。这说明字符串通过赋值,完成了所需内存分配、数据复制以及数据指针的填充。事实上,的确如此,背后都是OleAuto32.dll里的那帮API在管理VB/VBA的字符串,有兴趣的朋友可以查阅相关资料。所幸在VB/VBA中无需考虑这些细节,就像拖放窗口一样,都封装好了。

当重新给一个字符串变量赋值,会发生什么?不难发现,重新赋值时数据指针发生了变化。事实上,背后会调用一系列API函数,申请分配新的内存,拷贝数据,然后释放掉原占用内存。

如果在C中,上述行为都得自己实现,但在VB/VBA中你什么都不用做。是不是觉得VB/VBA中的字符串颇有几分好感呢?且慢,这个老好人,总有让你觉得讨厌的时候。


看一下变量,如果Dim strA As String后,未用过得到结果0。

Private Sub Command1_Click()
    Dim strA As String
    Debug.Print StrPtr(strA)
    strA = "aa"
    Debug.Print StrPtr(strA)
    strA = "bb"
    Debug.Print StrPtr(strA)
    strA = "c"
    Debug.Print StrPtr(strA)
    strA = "cd"
    Debug.Print StrPtr(strA)
    strA = ""
    Debug.Print StrPtr(strA)
End Sub

Private Sub Command2_Click()
    Dim strA As String
    Debug.Print StrPtr(strA)
End Sub

运行Command1和Command2结果:

 0 
 388466684 
 388467364 
 388467844 
 388466364 
 0

如果"aa"和"bb"长度一致,也有可能变地址,缩短也变地址。另外,看来strA = ""没啥用,还是被分配了地址,Private后,VB6自动收回了strA的地址,在Command2中变成了0。


比较表达式 Str="",算不算好的判断呢?你看背后做了哪些事就会明白:先给""常量分配一个临时内存,拷贝数据(无),然后调用字符串比较函数进行比较,最后返回结果,再释放分配的临时内存。这里面更别提编码转换之类的。可谓是绕了一个大圈子,才换来理解上的直白。

那有没有所谓更好的方法呢?其实从BSTR的结构入手即可。若要与Str=""等效,则用LenB函数进行判断。LenB函数就是直接获取BSTR结构体的Size成员的值。也即:if LenB(Str) then来代替,则简单高效了很多。


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