Object Pascal中String类型的内幕探讨 (转)
在object Pascal中,String(准确的说是AnsiString)是一种可变长度的字符串,通过PChar(AString)可以将其转换为与windows api相兼容的字符指针类型。事实上,String类型就是一个指针,你可以用Sizeof去读取它的大小,不论字符串的实际长度是多少,Sizeof(AString)永远是4。String与一般的Null-TeRminated字符指针不同的是,String还要保留另外的一部分空间,用于记录字符串长度和引用计数等信息。String类型在内存中的确切格式如下:XML:namespace prefix = o ns = "urn:schemas-microsoft-com:Office:office" />
(4字节)分配大小+(4字节)引用计数+(4字节)字串长度+(不定长)字符数组+(1字节)$0结束字符
为了验证这一点,我们可以在程序中添加一个作用域为private的String变量,在程序中动态改变它的长度和内容,同时观察它的分配大小和长度发生了什么变化。另外,为了观察引用计数的变化,只有在两个字符串互相复制的时候才能体现出来,我们在程序中也要实现这一点。
请新建一个Application,在窗体上放置一个Edit,一个ListBox和三个Button。其中,Edit用来改变字符串的内容;ListBox用来记录跟踪信息;三个按钮分别用于观察字符串的当前状况,观察字符串的引用计数变化情况和清空列表内容。
在Form的声明中添加一个变量:
type
TForm1=class(TForm)
...
private
s : string;
end;
添加三个按钮的事件处理如下:
procedure TForm1.Button1Click(Sender: TObject);
var
psz : PChar;
pdw : PDword;
dw1, dw2, dw3 : DWord;
begin
s := Edit1.Text;
psz := PChar(s);
pdw := PDWORD(psz);
Dec(pdw);Dec(pdw);Dec(pdw);
dw1 := pdw^;
Inc(pdw);dw2 := pdw^;
Inc(pdw);dw3 := pdw^;
ListBox1.Items.Add( Format('[Current]Size:%d, Ref:%d, Len:%d',
[dw1,dw2,dw3]) );
end;
procedure TForm1.Button2Click(Sender: TObject);
var
psz : PChar;
pdw : PDWORD;
dw1, dw2, dw3 : DWord;
s2 : string;
p1, p2 : Pointer;
begin
s := Edit1.Text;
psz := PChar(s);
pdw := PDWORD(psz);
Dec(pdw);Dec(pdw);Dec(pdw);
dw1 := pdw^;
Inc(pdw);dw2 := pdw^;
Inc(pdw);dw3 := pdw^;
ListBox1.Items.Add( Format('[Before assign]Size:%d, Ref:%d, Len:%d',
[dw1,dw2,dw3]) );
s2 := s;
p1 := Pointer(PChar(s));
p2 := Pointer(PChar(s2));
ShowMessage(Format('p1=%p,p2=%p',[p1,p2]));
psz := pChar(s);
pdw := PDWORD(psz);
Dec(pdw);Dec(pdw);Dec(pdw);
dw1 := pdw^;
Inc(pdw); dw2 := pdw^;
Inc(pdw); dw3 := pdw^;
ListBox1.Items.Add( Format('[After assign]Size:%d, Ref:%d, Len:%d',
[dw1,dw2,dw3]) );
s2 := s2 + 'Another string';
p1 := Pointer(PChar(s));
p2 := Pointer(PChar(s2));
ShowMessage(Format('p1=%p,p2=%p',[p1,p2]));
psz := pChar(s);
pdw := PDWORD(psz);
Dec(pdw);Dec(pdw);Dec(pdw);
dw1 := pdw^;
Inc(pdw); dw2 := pdw^;
Inc(pdw); dw3 := pdw^;
ListBox1.Items.Add( Format('[After COW]Size:%d, Ref:%d, Len:%d',
[dw1,dw2,dw3]) );
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
ListBox1.Items.Clear;
end;
如果你对指针的概念比较清楚的话,上面的代码是不难理解的。下面是该程序的输出结果:
[Current]Size:22, Ref:5, Len:5
[Before Assign]Size:22, Ref:2, Len:5
[After Assign]Size:22, Ref:3, Len:5
[After COW]Size:22, Ref:2, Len:5
观察上述结果,可以得出几个结论:
1.“分配大小”和“字串长度”之间存在着一种固定的数量关系,即分配大小=字串长度+17。为什么会有这种关系?请你再看一看String类型的内存分布:(4字节)分配大小+(4字节)引用计数+(4字节)字串长度+(不定长)字符数组+(1字节)$0结束字符,4+4+4+(strlen)+1,应该是13+(strlen)才对,也就是说应该还有4字节的空间,其用途尚不清楚。值得一提的是,如果你将字符串清空,那么Len的结果可能不是你所想象的0,而是一个让你大吃一惊的数字。
2.因为分配大小和字串长度都是用4字节来表示的,而且String类型是动态分配内存,所以字符串最大可能的长度应该是2^32-17个字节。
3.在拷贝字符串的时候,Object Pascal并不是把字符串简单的复制一份,而是采取了引用计数的方法,将两个字符串指向同一个内存空间,同时引用计数加1。当字符串变量被清除的时候,引用计数减1,如果引用计数已经减为0,表明该字符串可以真正被清除了。显然,这种方法比复制整个字符串的效率要高。
4.在给字符串赋值的时候,Object Pascal首先会检查字符串的引用计数是否为1。如果是,按照一般的方法直接赋值即可;否则,就说明有两个以上字符串指向同一个地址,这种情况就复杂多了。Object Pascal使用的是Copy-on-Write机制(COW),为当前字符串另外开辟一个缓冲区,将新内容拷入;同时,原来的字符串引用计数要减一。
5.知道了String的内存布局,我们也就知道了PChar(str)的意义了。不过,使用PChar的同时也就丢失了String的动态增长和引用计数的功能,所以一定要小心,另外要注意PChar长度的计算和字符串长度一定要同步,否则会出问题。比如,下面的代码就不能正常工作:
var
str : string;
begin
SetLength(str,256);
GetWindowDirectory(PChar(str),256);
str := str + ‘win.ini’;
end;
这样的结果是不正确的。之所以不正确,是因为SetLength将字符串长度设成了256,而PChar计算的长度只到第一个$0为止。正确的方法应该是:
SetLength(str,256);
GetWindowsDirectory(PChar(str),256);
SetLength(str,StrLen(PChar(str)));
str := setr + ‘win.ini’;
说明:上面的程序是在Delphi 5下测试通过的。Borland并不保证String的内存结构在以后的Delphi版本中会保持不变,所以,上述例子只是作为测试用,实际的程序中不应该这样使用String,谨此说明。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10797429/viewspace-101487/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/10797429/viewspace-101487/
Object Pascal中String类型的内幕探讨 (转)相关推荐
- Object Pascal 中类型
Object Pascal 中类型的一些注意 2010-04-16 14:15 --------------------------------------------------------- 原创 ...
- java语言特点 字符串不变_面试必问:Java中String类型为什么设计成不可变的?
这几天在各大平台上都看到过这样一些帖子,全都是关于String类型对象不可变的问题,当然现在也是找工作的准备时期,因此花了一部分时间对其进行整理一下. 想要完全了解String,在这里我们需要解决以下 ...
- c++ java string_C++中string类型的内部结构是什么?
C++中string类型的内部结构是什么? string a; 为什么sizeof(a)是32? ostringstream的问题 在代码中#include 了,声明了一个ostringstream变 ...
- java中数组的返回值是什么类型_面试必问:Java中String类型为什么设计成不可变的?...
这几天在各大平台上都看到过这样一些帖子,全都是关于String类型对象不可变的问题,当然现在也是找工作的准备时期,因此花了一部分时间对其进行整理一下. 想要完全了解String,在这里我们需要解决以下 ...
- java string设置编码_详解Java中String类型与默认字符编码
为什么写这个 至于为什么要写这个,主要是一句mmp一定要讲,绕了一上午,晕死 Java程序中的中文乱码问题一直是一个困扰程序员的难题,自己也不例外,早在做项目时就遇到过很多编码方式的坑,当时想填来着, ...
- java中字符串的创建_【转载】 Java中String类型的两种创建方式
本文转载自 https://www.cnblogs.com/fguozhu/articles/2661055.html Java中String是一个特殊的包装类数据有两种创建形式: String s ...
- java中将Object类型转换成String类型
1. String.valueOf(Object) 从下图(a图)的String类的valueOf(Object)的源码可以看到,当传入的值为null的时候返回的是"null"字符 ...
- 结构体中string类型成员
结构体中存在string类型成员 原创 2015年12月21日 17:34:04 标签: 结构体 / string类型成员 / 构造函数 2593 #include <iostream> ...
- java string类型_java中String类型
String类型是字符串类型..字符串一旦创建不可以在改变."abc"字符串对象一旦创建,不可以再改成"abcd" 提升字符串的访问效率:在程序中使用了&quo ...
最新文章
- 行为翻译模型是人脑的核心
- 9076什么意思_9076西南大学人力资源开发与管理答案
- 神奇的 SQL 之擦肩而过 → 真的用到索引了吗
- Java_案例实例1.简单的人机交互
- 如何在Java中实现线程池
- 计算机网络作业答案吴,中国大学《2020春季课程-计算机网络应用吴迪》答案全部2020高校邦《羽毛球》作业题库答案...
- flex 学习篇 ---- 导航类容器
- Fluid给数据弹性一双隐形的翅膀 (1) -- 自定义弹性伸缩
- java create 透明图片_Java 如何生成透明背景色的图片
- hdu 1862 EXCEL排序
- echarts4.8.0最新版本下载,亲测可用
- 基于 Spring Boot 的停车场管理系统
- 基于Bootstrap的下拉框多选 Bootstrap Multiselect 插件使用
- 浅谈Android之SurfaceFlinger相关介绍(一)
- 为什么需要api产品经理
- MapperReduce-----好友案列
- Linux中执行bash脚本报错/bin/bash^M: bad interpreter: No such file or directory
- composer windows 下面内存溢出 Allowed memory size of 1610612736 bytes exhausted
- 王者荣耀 ios 账号android,王者荣耀ios和安卓数据转移(手把手教你跨系统转移账号)...
- 菜鸟教程PHP学习笔记(不定期更新)