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

VB6中用CopyMemory拷贝字符串种种猫腻(一) 版权声明:可以任意转载,转载时请务必以超链接形式标明如下文章原始出处和作者信息及本声明 作者:xixi 出处:http://blog.csdn.net/slowgrace/archive/2009/09/14/4549926.aspx本文来自此帖的冗长讨论,感谢Tiger_Zhao的全程指点和陈辉、阿勇、马云剑等很多朋友的热心参与。本文其他部分在:(二)、()、(四)。话说VB6是个很认 阅读详情

版权声明:可以任意转载,转载时请务必以超链接形式标明如下文章原始出处和作者信息及本声明
作者:xixi
出处:http://blog.csdn.net/slowgrace/archive/2009/09/14/4550293.aspx

本文来自此帖的冗长讨论,感谢Tiger_Zhao的全程指点和陈辉阿勇马云剑等很多朋友的热心参与。本文其他部分在:()、()、()。

3.4 King06在10楼的代码——错误的代码正确的结果
再来看这段代码:
CopyMemory pString1, ByVal VarPtr(String1), 4
CopyMemory String2, pString1, 14
可以说和老魏的代码有异曲同工之妙。看第2个CopyMemory,又是从一个Long型变量地址拷14字节,我打眼一望,判断结果是乱码,可是没成想结果居然是浮肿型的非乱码。不废话,比葫芦画瓢,看下面的代码注释吧:
所以,10楼的代码也是属于瞎猫碰上死老鼠,“实际上是在胡乱操作内存”。不过这个代码没有老魏的代码狠。老魏的代码如果把变量次序换换就不能得到正确结果,甚至会VB挂掉。
而这个不会。这个变量次序无论怎么折腾,从pString1拷到的14字节中的头4字节总是String1的字符串缓冲区指针,也就是说_tmp2总是能得到这个指针,虽然后面的10字节就不定是啥了。这个代码貌似在拷字符串缓冲区,可实质上却是通过拷贝字符串缓冲区指针得到了基本正确的结果。这个乱啊……
 
3.5 我在34楼的代码——中英文混杂的字符串如何算字节数
看下面这段代码。
这段代码的输出结果是我有点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 暴强练习
这一小节我们来给出一堆上节代码的变体,并逐行给出解释(代码是我写的,赵老虎给出了超级清晰的逐行解释),来看看你是否全能理解。首先我们有如下声明和初始化:
其次对以下 每一对语句用如下语句在立即窗口观察结果:
Debug.Print String2_7 & "*", Len(String2_7), LenB(String2_7)
Debug.Print String2_14 & "*", Len(String2_14), LenB(String2_14)
来看如下语句和注释你是否都能看懂吧:
注:输出结果放在每个语句后的注释中。我在每个字符串的末尾加了个“*”输出结果,以便于数字符串末尾的空格。另外,上面赵老虎的解释中给出的'ANSI编码,实际上指的是_tmp2接受拷贝后得到的值;'Unicode编码则是指VB在结束API调用后String2最后得到的值。
 
3.5.2 长度不确定?
看上面第2小节赵老虎的详细注释,会发现有好几行的注释说String2的长度不确定。比如例5。这很有点奇怪,String2_14我们已经初始化过长度了啊?而且输出的结果是11和22,也是对的(因为14个字节里有3个中文字符,所以要减3)。那是不是赵老虎弄错了呢?来看赵老虎的解释吧。
 
Unicode-Ansi 转换时,确定 _Tmp 长度为 14 字节。Ansi-Unicode 转换时,只对这 14 字节进行转换,而 String2_14 根据转换的长度,会重新分配一个字符串,并不是说将结果直接写回旧的字符串内存中。这可以通过调用前后 StrPtr(String2_14) 的变化来确认。所以 String2_14 的长度受到最后 4 个不确定字节的影响。看起来长度确定只不过你的代码中最后 4 字节正好全 0 而已。
 
下面的例子说明 4 个不确定字节的影响。为了不溢出,只复制 14 字节。为了指定后面的字节,String1C 的 Unicode-Ansi 转换就自己模拟了。
补充提问
Q:_tmp在AU转换后的长度是谁确定的?
A:那当然是VB确定的。CopyMemory根本不知道转换这回事。
 
Q:_tmp在AU转换后的长度是如何确定的?是根据chr(0)字符么?
A:BSTR标准中每个字符串的字符串缓冲区之前的4个字节记录了它的长度啊。
 
3.5.3 如何查看字符串的编码
细心的朋友也许看到上节的注释会问,这些ANSI编码和Unicode编码是如何得到的啊?这个简单,有两种方法。一种是用ASC函数( 这篇博文的最后有详细介绍),另一种是把字符串赋值给Byte数组,然后逐个Hex查看。看以下的代码:
在立即窗口中键入GetUACode("我有点Slow"),可得到如下结果:
输出11-62-09-67-B9-70-53-00-6C-00-6F-00-77-00
6211-6709-70B9-53-6C-6F-77
CE-D2-D3-D0-B5-E3-53-6C-6F-77
CED2-D3D0-B5E3-53-6C-6F-77
我有点Slow
我有点Slow
第一行和第二行分别是用Byte数组和Asc函数得到的Unicode编码;第一行和第二行分别是用Byte数组和Asc函数得到的ANSI编码;第五行和第六行分别是用我们得到的Unicode编码和ANSI编码复原得到字符串。
 
从第二行的输出可以看出,汉字“我”的Unicode编码是6211,这从第五行的输出也可以验证。而从第一行的输出可以看出这个编码在内存里的存储方式是1162。想想我们前面提到的小端序,低位在前,这个结果就可以理解了。
 
可是的话,看ANSI编码又会费解了。比如观察第3行、第4行、第6行的输出,可以发现汉字“我”的ANSI编码CED2在内存里的存储方式是CED2,高位在前,怎么回事?不是说Intel架构的都是小端序么?
 
呵呵,这其实不是真正的大端序,而是因为多字符集编码的特殊规定导致的。因为对于汉字的ANSI编码而言,无所谓MSB, LSB。它就是把第一个字节理解为Leading Byte,第二个字节理解为另外的编码,所以它们在内存里的存放次序不能倒过来,否则就编码就不能得到正确解释了。( 这个帖子里有相关讨论,感谢AisaC)
 
3.5.4 插播:Debug.Print String1是不是蕴含了UA转换?
Q:Debug.Print String1是不是蕴含了UA转换?你看,String1在内存里显示的英文都是2字节的,每个英文之后都有个空格,可是打印出来却没有了。
A:没有。显示和编码两回事,String1就是Unicode编码的,显示的时候会按Unicode编码解释并显示它,1个两字节的英文字符仍然会按1个字符显示。
 
3.6 替换指针法——最有效率的方法?!
直接把String2的字符串缓冲区指针指向String2的,这样只需要拷4个字节,不是很妙么?
(1)最直接的想法是像下面这样,不过它涉及隐含的UA/AU转换
CopyMemory String2, String1, 4 'UA/AU转换'
(2)如果不想要这样隐含的UA/AU转换,那就不要传字符串参数,像下面这样:
CopyMemory ByVal VarPtr(String2), ByVal VarPtr(String1), 4 '无需UA/AU转换'
也可以像下面这样
CopyMemory ByVal VarPtr(String2), StrPtr(String1), 4 '无需UA/AU转换'
但是不能像下面这样,自己想为什么吧,呵呵。
CopyMemory StrPtr(String2), StrPtr(String1), 4 '目标地址不对
附上完整的代码:
这看起来是最有效率的字符串复制的方法,只需要拷贝4个字节。但是,这样做是 不对的,不仅是不对的,而且是 危险的。因为,在VB中字符串和字符串缓冲区是一对一的,如果把两个字符串变量的缓冲区指向同一个地方,就会导致重复释放同一块内存,而这会引起不可预期的错误。而同时,由于String2原先的缓冲区不再被VB记住,这又会导致内存泄漏。不过,据 赵老虎说,CopyMemory String2, String1, 4这种写法可以在参数声明为String时正常使用,待考证。
补充提问
Q:那如果我们把String2 = String$(7, "0")这一句拿掉,不给它显式初始化,还会导致重复释放内存和内存泄露么?
A:内存泄露是不会了。但是重复释放内存还是会的。因为在程序接受后,VB并不需要检查字符串变量是否被初始化,只要看有指针值,就要释放字符串缓冲区的空间。

WEI WAN DAI XU

vb6提速技巧之一 等长的和非等长的大数组的复制 由于vb6的先天缺陷,导致我们在vb6中构造某些常见的数据结构非常困难,比如要在vb6中构造一个链表。而有些情况下,由于程序的需要,我们需要这种可变长的有序集合,当然,vb6中也有集合,可以用for  each来遍历,但是由于这种集合的无序性,使得我们在处理大数据量的时候效率变得十分低下。很多时候,我们真希望数组有集合的某些的性质,比如,从数组中移除某些元素后,它又自动生成一个连续的数组,可惜,v 阅读详情

相关推荐

SKF SimPro Quick 4.8.1发布

发布说明可以在SimPro手册的SKF SimPro Quick应用程序的一般部分下找到(帮助→SKF SimPro快速手册→一般→发布说明)。请删除旧版本,请备份您存储在C:\SKF SimPro xxx\files文件夹中的文件以及所有自定义报告模板和表格。我们很高兴地通知您,我们的轴承应用工程软件SKF SimPro Quick 4.8.1可供下载。将新的安装文件下载到本地计算机后,双击它安装向导将打开(按照说明操作)3.安装后,在本地机器上启动SKF SimPro Expert软件。

小韩的博客 1774

VB6中用CopyMemory

VB6中用CopyMemory拷贝字符串种种猫腻

人工智能训练师级考试大纲深度解读|理论+实操分值分布+六大模块占比

级人工智能训练师考试分为理论和实操两大模块(各100分,合格线60分)。理论考试包含AI基础(30%)、数据标注(25%)、模型训练(20%)、测试(15%)和系统运维(10%),题型为客观题;实操考核侧重数据标注(40%)、模型测试(35%)和系统运维(25%),采用现场操作形式。复习应优先关注AI基础和数据标注等高分值模块,实操需重点掌握标准流程和结果准确性(占评分70%)。考试要求两模块均达标,理论侧重概念理解,实操强调规范执行与文档完整。

白话机器学习 615

CopyMemory函数

CopyMemory函数 下面我们来熟悉一下本文重点讨论的这个函数。 [vb] view plaincopyprint? Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _      (pDest As Any, pSource As Any, ByVal byteLen As Long)  

whchensir的专栏 1万+

VB6中用CopyMemory拷贝字符串种种猫腻(四)

版权声明:可以任意转载,转载时请务必以超链接形式标明如下文章原始出处和作者信息及本声明 作者:xixi 出处:http://blog.csdn.net/slowgrace/archive/2009/09/14/4550530.aspx本文来自此帖的冗长讨论,感谢Tiger_Zhao的全程指点和陈辉、阿勇、马云剑等很多朋友的热心参与。本文其他部分在:(一)、(二)、()。第四节 如何用Co

3661

VB6中用CopyMemory拷贝字符串种种猫腻(二)

版权声明:可以任意转载,转载时请务必以超链接形式标明如下文章原始出处和作者信息及本声明 作者:xixi 出处:http://blog.csdn.net/slowgrace/archive/2009/09/14/4550116.aspx本文来自此帖的冗长讨论,感谢Tiger_Zhao的全程指点和陈辉、阿勇、马云剑等很多朋友的热心参与。本文其他部分在:(一)、()、(四)。第节 经典错误代

5401

linux网络编程 copymemory,在VB6中用CopyMemory拷贝字符串种种猫腻(一)

'正确的ByValString的用法OptionExplicitConstSTR_E="PowerVB"PrivateString1AsStringPrivateString2AsStringPrivatepString1AsLongSubtest7()DimString1AsStringDimString2AsString'Dim_tmp1AsString,_tmp2As...

weixin_39593247的博客 420

内存泄露,缓冲区溢出,堆破坏 的问题

今天在用c语言写socket的AES加解密的时候,由于是直接操作内存,错误百出,调了整整天才把加解密的问题给搞出来。。。。整死我了。。。 总结一下,是自己使用malloc动态分配内存不当,没有掌握sizeof 的用法, 还有没有深刻理解strcpy, strcat函数导致的。 1.使用malloc动态分配内存之后,一定要把内存free掉,然后指向NULL,不然会造成内存泄露 一般我们常说的

Miibotree 3827

VB中利用CopyMemory使用指针

  一、指针是什么?   不需要去找什么标准的定义,它就是一个32位整数,在C语言和在VB里都可以用Long类型来表示。在32位Windows平台下它和普通的32位长整型数没有什么不同,只不过它的值是一个内存地址,正是因为这个整数象针一样指向一个内存地址,所以就有了指针的概念。   有统计表明,很大一部分程序缺陷和内存的错误访问有关。正是因为指针直接和内存打交道,所以指针一直以来被看成一个危险的东

wjb9921的专栏 2821

VB 6.0 利用CopyMemory实现 指针功能

工作需要,要用VB写一个接口程序,其中要把浮点型转成Byte数组,用到了一个API,先记录下来,以后C#中可能会用到同样的功能。 PrivateDeclareSubCopyMemoryLib"kernel32"Alias"RtlMoveMemory"(ByValDestinationAsLong,ByValSourceAsLong,ByValLen...

Liumei_1122的博客 427

VB6中实现浮点数各个字节的提取

前面已经讲过如何在vb6中实现将多个字节还原成其实际的原始类型,那么我们也经常需要在上位机上将浮点数等数据按照字节进行传输。 废话不多说了,直接给出系列源码,请参考之前的文章来看吧。   Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any,

SANTOOK的专栏 4664

使用 C 突破 VB6字符串长度限制

VB6 代码是必需要维护的“遗产”,但公司之前使用 VB 开发的产品一直都在不定期地蹦出“长期不可解”的 Bug,维护人员捣浆糊和客户的抱怨成为经年的常态。最近痛下决心欲把公司之前使用 VB 开发的所谓的“中间件”或者“中间层”的组件服务以及蹩脚的 Socket 通讯服务换成 webservice ,期望能够解决大部分的问题。但是目前还没有计划把客户端的 VB 代码替换掉,于是使用 C 开发了与

mmxx0212的专栏 3597

一次实验,弄明白了VB6中的字符串存储格式

    虽然看过《老兵新传》《VisualBasic win32编程》中介绍vb字符串的内容,但依旧对vb中的字符串如何存储不十分清楚,不像对C语言中的字符串那样,在头脑中有一个清晰的轮廓。同时对varptr(),strptr()的理解也只限于书上的那些例子。直到有一天,突然灵光一闪,动手写了下面的程序,当程序执行的结果出现时,我也终于恍然大悟。Option Explicit Private De...

kingdragonfly的博客 1468

VB字符串数组快速复制的一种方法

Option ExplicitPrivate Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal numBytes As Long)Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMem...

weixin_30835649的博客 1548

【毕业设计】基于爬虫的房源数据分析系统.zip

基于Python的南京二手房数据采集及可视化分析内容简介首先通过爬虫采集链家网上所有南京二手房的房源数据,并对采集到的数据进行清洗;然后,对清洗后的数据进行可视化分析,探索隐藏在大量数据背后的规律;最后,采用一个聚类算法对所有二手房数据进行聚类分析,并根据聚类分析的结果,将这些房源大致分类,以对所有数据的概括总结。通过上述分析,我们可以了解到目前市面上二手房各项基本特征及房源分布情况,帮助我们进行购房决策。应用技术介绍Python网络爬虫技术RequestsBeautifulsoupPython数据分析技术NumpyMatplotlibPandas

上一篇: 在VB6中用CopyMemory拷贝字符串的种种猫腻(二)
下一篇: 在VB6中用CopyMemory拷贝字符串的种种猫腻(四)
slowgrace
博客等级 码龄18年 4613粉丝 205原创
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值