2024年6月15日发(作者:)

在VB6中用CopyMemory拷贝字符串的种种猫腻(三) - slowgrace的专栏 - CSDN博客

在VB6中用CopyMemory拷贝字符串的种种猫腻(三)

收藏

版权声明:可以任意转载,转载时请务必以超链接形式标明如下文章原始出处和作者信息及本声

作者:

xixi

出处:

/slowgrace/archive/2009/09/14/

本文来自此帖的冗长讨论,感谢Tiger_Zhao的全程指点和陈辉、阿勇、马云剑等很多朋友的热心

参与。本文其他部分在:(

一)、(二)、(四)。

3.4 King06在10楼的代码——错误的代码正确的结果

再来看这段代码:

CopyMemory pString1, ByVal VarPtr(String1), 4

CopyMemory String2, pString1, 14

可以说和老魏的代码有异曲同工之妙。看第2个CopyMemory,又是从一个Long型变量地址拷14字

节,我打眼一望,判断结果是乱码,可是没成想结果居然是浮肿型的非乱码。不废话,比葫芦画

瓢,看下面的代码注释吧:

1. Sub test3_King06()

2. Dim pString1 As Long

3. Dim String2 As String

4. Dim String1 As String

5. '这 3 个变量每个长 4 字节,在栈上按地址从低到高为:

6. '| pString1 | String2 | String1 |

7.

8. String1 = STR_E

9. String2 = String$(7, 0)

10.

11. 'Try three: Get the string's pointer from VarPtr

12. CopyMemory pString1, ByVal VarPtr(String1), 4 '得到String1字符串缓冲区指针

13. CopyMemory String2, pString1, 14

14. '由于UA转换,生成了新的临时变量_tmp2,所以现在栈上的内容如下

15. '| _tmp2 | pString1 | String2 | String1 |

16.

17. '由于不加 ByVal,其实是从变量 pString1 的地址向变量 _tmp2 的地址复制 14 个字节

18. '等于直接操作栈内存,让变量向左复制,于是

/slowgrace/archive/2009/09/14/(第 1/9 页)2011-3-31 15:33:08

在VB6中用CopyMemory拷贝字符串的种种猫腻(三) - slowgrace的专栏 - CSDN博客

19. '| _tmp2 : 原 pString1 的值,也就是String1字符串缓冲区指针,汗

20. '| pString1 : 原 String2 的字符串指针

21. '| String2 : 原 String1 的字符串指针

22. '| String1 : 不确定 |

23.

24. '现在_tmp2里居然得着String1的字符串缓冲区指针了

25. '那么调用之后,强行AU转换,String2得到浮肿的结果,也完全可以理解了

26.

27. String2 & "*" '得到的是 P o w e r V B *

28. End Sub

所以,10楼的代码也是属于瞎猫碰上死老鼠,“实际上是在胡乱操作内存”。不过这个代码没有

老魏的代码狠。老魏的代码如果把变量次序换换就不能得到正确结果,甚至会VB挂掉。

而这个不会。这个变量次序无论怎么折腾,从pString1拷到的14字节中的头4字节总是String1的字

符串缓冲区指针,也就是说_tmp2总是能得到这个指针,虽然后面的10字节就不定是啥了。这个

代码貌似在拷字符串缓冲区,可实质上却是通过拷贝字符串缓冲区指针得到了基本正确的结果。

这个乱啊……

3.5 我在34楼的代码——中英文混杂的字符串如何算字节数

看下面这段代码。

1. Dim String1C As String

2. Dim String2_7 As String

3.

4. String1C = "我有点Slow"

5. String2_7 = String$(7, 0)

6.

7. CopyMemory ByVal String2_7, ByVal String1C, 14

8.

9. String2_7 & "*", Len(String2_7), LenB(String2_7)

这段代码的输出结果是我有点S*,没能完全地把“我有点Slow”这个字符串拷出来。这是为什么

呢?其实用上面各例的原理完全可以理解这个结果。只要记住无论是Unicode还是ANSI编码,中

文字符始终是维持双字节一个字符。而英文字符则是在UA转换时,将2个字节缩减为1个字节;

AU转换时,将1个字节扩张为2个字节。所以,上面的CopyMemory的实际执行过程是:

(1)首先String1C“我有点Slow”被转换为ANSI字符串_tmp1共10字节,所以拷14字节的话,最

后4字节是不确定的,不定是啥。

(2)其次String2_7被转换为ANSI字符串_tmp2共7字节。拷14字节到_tmp2,后7字节会溢出(这7

字节中的最后3字节内容还不确定)。因此_tmp里只有前7字节,就是"我有点S"。

(3)最后VB把_tmp2转到String2,这是AU转换,只最后填个0,其他长度不变化。因此最后,

LenB(String2)=8

3.5.1 暴强练习

这一小节我们来给出一堆上节代码的变体,并逐行给出解释(代码是我写的,赵老虎给出了超级

/slowgrace/archive/2009/09/14/(第 2/9 页)2011-3-31 15:33:08