很奇怪的String指针问题!!! ( 积分: 100 )

  • 主题发起人 主题发起人 kp1150
  • 开始时间 开始时间
呵呵学习,大家钻的这么深。
 
我来说几句,首先我觉得,lich对字符串的解释很有道理的

现在来说说kp1150认为的:在读字符串的某个地址的时候不引起copy-on-write操作,而只是在改变了字符串中某个字符的时候才引起copy-on-write。
假设kp1150说的是对的,那么我们设想这么一个场景
var
s, s1, s2, s3,s4: String;
p: PChar;
begin
s := '12345';
s1 := s
s2 := s
s3 := s
s4 := s
//这个时候,内存中只有一份字符串
p := @s1[2];//假设这句没有引起copy-on-write

//按照我的意思,我仅仅是要改变s2的第2个字符为a
p^ := 'a'
//执行到这句,应该引起copy-on-write了吧,再确切点说,s2应该被独立出来了吧
//但是问题出来了,如何知道这个p来自s2而不是s,s3,s4呢?
//或许你会说,因为p=@s1[2];这个付值,只要记录这个付值操作就行了,那是不是说PChar类型或者其它的指针类型需要记录我的这个指针来自哪里呢?这样的记录,开销可就太大了
end;

所以说,最保险的做法,就是在p := @s1[2];这句的时候,就先复制出来一个s2的副本,然后再把s2[2]的地址给p,这样,以后就不用管你拿着p到点干了什么事情了
 
楼上的才真的说到了点上,

不过,试问,要修改一个字符串中的字符
为什么不直接去修改, 而要先去取其地址,然后再修改呢?

不过有一点要注意了,
如果你要取第一个字符的地址, 直接用PChar 强制类型转换就可以了
如果用 @s[1] 可是会引起字符的引用计数的检查的
 
我是就问题说问题,临时写的程序,上面的代码只是一个给kp1150的一个反证法[:D]
 
顺着lich的思路,我发现了一个可能会导致不容易发现的错误的函数调用,模型如下:
procedure abc(Value: String);
var
p: PChar;
begin
p := PChar(Value);
p^ := 'a';
end;

var
s, s1, s2, s3, s4: String;
begin
s3 := '1234';
s4 := '5678';
s := s3 + s4
//这里产生了内存复制
s1 := s
//这里没有产生内存复制
abc(s1);
end;
上面的代码,按照设计的思路,应该是在调用abc函数之后,改变的是s1这个字符串的第一个字符为'a',但是,在调用完abc之后,s的内容也被改变了!!
也就是说,从s到s1再到参数Value,Delphi始终没有进行copy-on-write,操作Value也就是操作s本身。但是从代码的形式上看,s1 := s;的本意就是把s的内容给s1,而从函数abc的声明上看,是传值调用,应该是把s1的值(也就是字符串本身)复制一份给Value,从逻辑上,操作Value不应该对s1或者s有影响,但是,实际上确实有影响。
这个错误应该是很隐蔽的!
如果把上面的abc函数换成下面的函数
procedure abc(Value: String);
var
p: PChar;
begin
p := @Value[1];
p^ := 'a';
end;
那么s1和s的内容都不会被改变了。
这应该说是Delphi在进行copy-on-write时候没有考虑到的遗漏呢,还是应该说本来就是这个样子呢?
(以上的程序仅仅是临时写的测试程序,基本没有什么应用的价值,算是为了测试而测试吧)
 
p := PChar(Value);
你这样转换和
var
p: PChar absolute value;
begin
p := Value;
p^ := 'a';
end;
就没区别了 你使用的还是以前的地址 而不是拷贝
qq群:11105482
 
放飞 的理解力和创造力的确不凡,

既然是通过引用计数实现的内存自动管理,向用户(程序员)屏蔽了实现细节

但骇客(骇客帝国中的醒悟者,了解世界本质的人)是可以证明这种机制的存在的

有好处,必然也会有一些不好的地方,经验丰富的人,
会有意识或无意识的避开一些问题,或者在查找错误的时候,
也多了一条发现问题的思路
 
不好意思,这几天网络当掉了,所以。。。。
很认真的看了各位大吓的心得,收益非浅!
结帖!
 
又是一帖对String深入讨论的经典帖子,受益非浅!留个记号。[:D]
 
多人接受答案了。
 
后退
顶部