◆◆PChar用法问题,为什么我没分配内存,也一样可以用啊?◆◆(20分)

  • 主题发起人 小笨苯
  • 开始时间

小笨苯

Unregistered / Unconfirmed
GUEST, unregistred user!
书上说,PChar是一个指向字符串的指针,主要是为了使用C/C++的DLL,或WindowsAPI,起到
兼容作用的。说使用PChar时,必须先分配内存,再赋值,使用后再释放内存。可我下面这
一小段程序,并没有分配内存呀?为什么也能编译成功,而且执行正确呢?
procedure TForm1.Button2Click(Sender: TObject);
var
strTmp: AnsiString;
pstr: PChar;
begin
{ do something }
strTmp := 'I am a Chinese';
pstr := PChar(strTmp);
strTmp := '';
Caption := pstr;
end;
 
这样当然可以用啦,虽然你没有给 PChar 分配内存,但是它不需要另外分配内存了,
因为它指向了别人的空间(内存):)

 
说得对,如果,你给他赋一个"abc"
可能就不行了
 
多谢两位啦[:)]
可事实不象你们说的那样啊![:(]
我用的是Delphi5 + Win2000 + sp3
beta:
你说“因为它指向了别人的空间”,那么就我上面的程序来看,你的意思应该是pstr指向了strTmp
的地址,可是,正如我上面那样,strTmp := '';,那么结果也应该是空字符串啊,可显示
出来的还是'I am a Chinese'!到底哪不对呀?你再帮我看看吧
coolbaby:
我给它直接赋值字符串,pstr := 'I am a Chinese';也没什么问题呀!编译、运行都正常啊,
就是显示'I am a Chinese'
 
具体内容可以查看delphi5.0 开发人员指南 上面有非常详细的解释,
如果没有告诉我,我回头给你帖上,现在有事情!
对不起!
 
我是这样理解的:
PChar就是指向Char的指针,如果直接赋值,如pstr := 'I am a Chinese'就等于开创了一块内存,内容是'I am a Chinese',再让pstr指向这块内存的头地址。
 
我的理解:
定义Pchar就是定义了Array of Char;
pChar指向了Array of Char数组的首地址

你的例子我这样理解
procedure TForm1.Button2Click(Sender: TObject);
var
strTmp: AnsiString;
pstr: PChar
// 等于 Astr:Array of char;
begin
{ do something }
strTmp := 'I am a Chinese';
pstr := PChar(strTmp);
// 上面这句等于 SetLength(Astr,Length(strTmp));
// 然后,Move(strTmp,Astr[0],High(Astr))

// 最后pstr指向新的数组的地址
strTmp := '';
Caption := pstr;
end;
 
PChar指向的好像不是别人的内存空间。
procedure TForm1.Button2Click(Sender: TObject);
var
strTmp: AnsiString;
pstr: PChar;
begin
{ do something }
strTmp := 'I am a Chinese';
Edit1.Text:=IntToStr(Integer(@strTmp))
//strTmp的地址
pstr := PChar(strTmp);
 Edit2.Text:=IntToStr(Integer(@pstr));  //pstr的地址
strTmp := '';
Caption := pstr;
end;

 
去看这个函数的帮助就知道了:
PChar()
 
procedure TForm1.Button1Click(Sender: TObject);
var
a: PChar;
b: String;
begin
b := 'I am a chinese!';
a := PChar(b);
ShowMessage(Format('%p %p', [Pointer(a), Pointer(b)]));
b := '';
ShowMessage(Format('%p %p', [Pointer(a), Pointer(b)]));
end;

你看一看前后A和B两个指针的变化,就知道Delphi是怎么处理的了。
 
to 小八哥:
Edit1.Text:=IntToStr(Integer(@strTmp))
//strTmp的地址
pstr := PChar(strTmp);
 Edit2.Text:=IntToStr(Integer(@pstr));  //pstr的地址
你这段代码不对,你用了 @ 取地址,可是 strTmp 和 pstr 是两个不同的变量,
当然地址不一样了。注意,指向的地址和本身的地址是两回事:)
你改成这样:
Edit1.Text:=IntToStr(Integer(strTmp))
//strTmp指向的地址
pstr := PChar(strTmp);
 Edit2.Text:=IntToStr(Integer(pstr));  //pstr指向的地址
就一样了吧:)

to 小笨苯:
//strTmp := '';,那么结果也应该是空字符串啊,可显示
//出来的还是'I am a Chinese'!到底哪不对呀
上面讲了,它们的确是指向的同一个地址,但那是在你修改 strTmp 之前,
实际上你的 pstr := PChar(strTmp) 是被 Delphi 认为是一次字符串赋值的,
不过由于你这样赋值,它们的字符串肯定相同,没有必要使用两片空间存放
同一个字符串。所以这样它们指向同一片空间!
不过你一旦修改了 strTmp,那么 pstr 就和 strTmp 不同了。刚说了,那是
一次字符串赋值,所以,pstr 就必须另外申请一片空间来存放新的字符串。
于是两者就指向了不同的地方。这就是 Delphi 的 CopyOnWrite 技术。

呵呵,不知道说清楚没有,继续。
 
你做一下如下测试就应该知道了:
strTmp := 'I am a Chinese';
pstr := PChar(strTmp);
ShowMessage(Format('%x, %x', [Integer(strTmp), Integer(pstr)]));

strTmp := strTmp + 'Hello';
ShowMessage(Format('%x, %x', [Integer(strTmp), Integer(pstr)]));
 
我本来就比较笨,看了beta绕口令般的解释后,更晕了@_@ [:(]
不过,好像是稍微明白一点了,我再品味一下吧[:)]
 
谁能帮我解释一下“AnsiString字符串在内存中到底怎样存储”这个概念,好吗?
书上是这样说的:长字符串类型的变量实际上是一个32位的指针,指向一块动态的区域.如果
长字符串是空的,则值是Nil.对长字符串赋值实际上一个指针,多个变量同
时引用一个长字符串,互不影响.
procedure TForm1.Button4Click(Sender: TObject);
var
strTmp: AnsiString;
begin
strTmp := 'aaaaaa';
Caption := Format('%p', [Pointer(strTmp)])
//1
strTmp := 'abbbbb';
Caption := Caption + ' ' + Format('%p', [Pointer(strTmp)])
//2
end;
为什么//1和//2处的地址不一样呢?按我想,只不过值变了,首地址为什么会变呢?哪位
大哥给我讲一下字符串的存储机制是怎样的,谢谢啦![:(]
 
看来一变值这个变量在内存里的地址就变掉了
 
//看了beta绕口令般的解释后,更晕了@_@
呵呵,那是我的错,说的不清楚。

这样说吧:
首先,明白一点,基本上任何时候(你现阶段不用考虑特殊情况),你都可以把一个
PChar 当成一个字符串使用。因此,上述情况可以理解为两个 string 之间的赋值
情况。这可以接受吧。

现在假设两个人A,B。A 现在电视 TA 上看电视:CCTV2。现在 B 也想看 A 正在
看的电视节目。你有两种办法:再开一台电脑 TB,也放CCTV2;另一个办法,让 B
直接看电视 TA 上的内容。当然后一种方法更好。
因此,如果 A 和 B 都不改变主意,大家就可以和平共处。但是如果 A 想看CCTV5,
那么他要是伸手一换,那么他自己是享受了;B 就受苦了。于是当 A 想换台的时候
由于有 B 在,就只能自己去另外开一台电视 TB(到有意见分歧的时候,才去开新的
电视),看CCTV5。
这就是 CopyOnWrite 的思想。不冲突情况下,可以节省一台电视(电费?:p

现在把上面的例子套用过来:
var
A, B: string;
begin
A := 'CCTV2'
// A 指向的地址是电视 TA,内容是 CCTV2
B := A
// B 也要看,直接指向 TA,于是也可以看到 CCTV2
A := 'CCTV5'
// A 改变主意,由于 B 在这里看 CCTV2,于是 A 不方便直接
// 换台(修改电视 TA 的内容),于是只有新开一台电视
// 所以现在 A 指向了电视 TB。

// 也就是说,采用 CopyOnWrite 技术,可以实现两者共享空间(也可以节省字符串
// 拷贝的时间,这是主要原因);但同时在出现冲突的时候,也不会相互影响。

// 正是因为这个“不会相互影响”,所以你那个例子,修改一个过后,另一个不变
end;

你的,明白?:)
 
'I am a Chinese'在程序被装入内存执行时已经获得了空间
 
我没看完,太多人了。但 beta 说的 CopyOnWrite 技术 ,其实就是这个
的原因,没有什么其它的。
真的可以去先看看《delphi5.0 开发人员指南 》就可以比较好了解了。
现在有《delphi6.0 开发人员指南 》出版与上本不些不同!
 
非常感谢beta多次不厌其烦的帮助,谢谢啦!
现在的情况是这样的:AnsiString字符串的值在改变以后,其变量的地址会改变;将AnsiString类型的变量
赋值给PChar指针,然后在改变字符串的值,地址也会改变。这些都是运用了Delphi所谓的“Copy On Write”
技术,至于,“Copy On Write”这个技术是怎样一个实现机理,我也无意去搞清楚了,总之,就是为了优化
程序而发明出来,为的就是尽量避免整个赋值字符串,不仅提高效率,而且节省内存资源。
但下面的代码,我还是百思不得其解,望执教
procedure TForm1.Button2Click(Sender: TObject);
var
pstr: PChar;
begin
pstr := 'I am a Chinese';
Caption := pstr;
end;
这到底是怎么一回事呢?为什么没有分配内存,却可以赋值,这个地址又是指向哪里的呢?怎样分配的内存呢?
 
'I am a Chinese'是一个字符串常量!在编译的时候就就已经编译到exe里了,并已经分配了
地址(相对地址)。当你的程序被shell装入内存执行的时候,它就有了绝对的内存地址(这时
pchar就可以赋值为这个内存地址,这样pchar就指向这个字符串了)。
另外即使你不触发TForm1.Button2Click(Sender: TObject),这个'I am a Chinese'也是在
内存里的,并不是动态创建的

这个问题在C语言书上基本上都有的讲
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
637
import
I
D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
顶部