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

K

kp1150

Unregistered / Unconfirmed
GUEST, unregistred user!
procedure TForm1.Button1Click(Sender: TObject);
var
s,s1: String;
p,p1: Pointer;
begin
s := 'abcdef';
s1 := s;
p := Pointer(s);
//showmessage(IntToStr(integer(@(s[1]))));{这句话注不注释掉会有影响么}
showmessage(IntToStr(integer(s)));//S地址
showmessage(IntToStr(integer(s1)));//S地址
showmessage(IntToStr(integer(p)));//S地址
showmessage(pchar(p)^);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
s,s1: String;
p,p1: Pointer;
begin
s := 'abcdef';
s1 := s;
p := Pointer(s);
//showmessage(IntToStr(integer(@(s[1]))));{这句话注不注释掉会有影响么}
showmessage(IntToStr(integer(s)));//S地址
showmessage(IntToStr(integer(s1)));//S地址
showmessage(IntToStr(integer(p)));//S地址
showmessage(pchar(p)^);
end;
 
我测试的结果是会有影响!
如果注释掉的话,后面三个所显示的地址都一样!
可是如果不注释掉的话,则(第一个show等于第二个show) <> (第三个show 等于第四个show)
 
s, s1 ,p 所指向的地址应该都是相等的啊,@s[1]也是s的首地址应该也是相等的!
为什么会因为//showmessage(IntToStr(integer(@(s[1]))))有没有注释掉而改变了?
 
有影响。楼主钻得很深啊,去看有关string、@和指针操作的相关知识就知道为什么了,关键是串复制部分。
 
对字符串中的字符的取地址,会引起一个 UniqueString 过程的调用

这个过程保证这个字符串的引用计数为1

你前面是个常量字符串,引用计数为 -1,
所以,这个过程复制了一个新的字符串,其引用计数为1

所以,如果不注释的话, s的地址会发生变化, 所有对s的直接引用地址都是相同的
而第三个和第四个是在调用这个过程前取到的,是变化之前的值
 
对字符串内的字符的取地址和写操作,都会引起那个过程的调用
这叫做写时复制,保证引用计数为1

而对字符串内字符的读取是不会引起这个操作的
 
自由界面和报表的完美解决方案!
http://www.anylib.com
 
When indexing is used to change the value of a single character in a string, a copy of the string is made
也就是说当S有变动时才会复制一个新的串出来
可是我现在的程序是什么都没有做,为什么地址会变化了?
 
为什么取字符串中字符地址,会导致重新分配内存来复制字符串了!?
就跟copy-on-write一样在改变后才复制不是很好么?
 
呵呵, 取一个字符串中的字符的地址, 如果不是为了修改字符
那还能做什么,

Delphi假设你取某个字符的地址,就是为了修改这个字符
通常都不会错的

如果Delphi的假设没有命中,也不会有不好的后果
引用计数以及写时复制, 目的是向用户屏蔽这个实现细节,
让你感觉不到这种机制的存在

上帝创造世界的时候,要高明多了,
让这个世界无法感觉到的他的存在
 
lich最后的比喻牛!呵呵。
 
老问题了

---=== 原理 ===---
AnsiString的结构为:|分配的大小|引用计数|长度|DDG|#0|,赋值的时候引用计数不变,改变值时引用计数++,同时实际分配新的内存空间
string 就是字符串,只是赋值时不copy,而copy 它的指针,当改变时才copy
,这只是一种优化技术,s1:=s2,你就可以把它当作完全地copy .其实什么变量没有指针啊?
哪不过是编译器的事,我们就把它当作一个字符串变量就可以了。

---=== 形象的解释 ===---
var s,si: string;
'1234' 存在内存中某个地方,它是属于s的,s这个变量就存有'1234' ,
现在s1 := s
那么本应copy '1234'这个内容 给s1,但是这样太浪费了,
于是delphi让它们临时都用 '1234'这一个字符串,等到它们任何一个变量
被赋成其它值时才把它们各自分开,比如s1:='123456',这时它们才分家了。
 
VictorWoo:
你说的东东我是知道的,就是copy-on-write机制!
我问题的关键是我只是取字符串中某个字符的地址,并没有改变它,为什么也会引起整个字串的重新拷贝!
lich:
我就爱钻牛角尖,编译器为什么不判断一下字符串是否改变(又不会耗时),然后才做动作了?如果一取地址就会引起整个字串的重新COPY,那岂不是既废时又耗空间(字串理论上可是可以装上G的东西哦)!?
 
实际上编译器根本就没有办法判断,不是吗,

如果让你来判断,你怎么判断呢?

如果嫌耗费时间, 没什么事,干吗要去取字符的地址呢?
取字符的值难道不够吗?
 
lich:
为什么没有办法判断!?编译器为字符串分配的内存区,它应该比我们更容易知道这块内存区的内容到底有没有变动啊!?
 
难道你每次用一个指针去修改字符串中的一个字符时,
编译器都要生成代码去遍历内存中的所有字符串,看这个字符属于哪个字符串吗?

而且,Delphi运行时,也没有维护一个所有字符串的列表啊,也没有办法进行遍历的

说说你的方法吧, 我觉得编译器根本就不能进行预测
 
关键是你把这个地址作为了函数参数,编译器是不可能知道这个函数是否会改变它的值,所
以只好保险起见了。编译器是不可能知道哪些函数可能改变它的值的,要知道,库函数已经
函数太多了,而且你还可以有无穷个自定义函数。
 
procedure TForm1.Button1Click(Sender: TObject);
var
s, s1: String;
begin
s := 'abcdef';
s1 := 'abcdef';
if 0 = integer(@s[1]) then;//语句 A
if integer(s) = integer(s1) then ShowMessage('=') else ShowMessage('<>');
end;
 

Similar threads

I
回复
0
查看
538
import
I
I
回复
0
查看
662
import
I
I
回复
0
查看
577
import
I
I
回复
0
查看
672
import
I
顶部