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

  • 主题发起人 小笨苯
  • 开始时间
也许看看编译原理的知识对你有帮助[:D]
 
不要看你那本书上的东西。
说什么指针的,容易把人搞糊涂。现在很多书都尽量的避免指针概念了。
你看看C语言对你应该有帮助的。
你用Pchar只用把它当一个函数来用就可以了。以后用着用着你就会发现其中的原因了。
不必去刻意搞懂其中的原因,多用到会用,你就会知道了。
 
5555555555555555
我觉得舞雪说得有一定道理,但我还是想尽量把它弄明白[:(]
 
我再说两句废话
DELPHI为了优化代码
对string的处理是
如果pchar=string
那二者指向相同的地址和内存
如果string改变了发生了写操作
那么DELPHI处理write_copy
再为string申请一个空间的
而原来的pchar则不变
但如果是pchar发生的改变则不wirte_copy
直接修改string的值
不知这样明白否
 
不能没有你:
我要问的是
下面的代码,我还是百思不得其解,望执教
procedure TForm1.Button2Click(Sender: TObject);
var
pstr: PChar;
begin
pstr := 'I am a Chinese';
Caption := pstr;
end;
这到底是怎么一回事呢?为什么没有分配内存,却可以赋值,这个地址又是指向哪里的呢?怎样分配的内存呢?
 
你真是有点那个啊,给你解释了你不看[:(]
 
gxcooo大哥,你别生气,不是不相信你,我想多听几个人的观点
 
呵呵,服了你了[:D]
我估计向你那样的常量字符串在编译的时候不会用string类型存放的
 
gxcooo大哥:
这个常量应该是分配在进程的“栈”中吗?
 
这种常量肯定在程序的数据段内
 
gxcooo大哥:
我水平低,分数又少,所以,提问题也不能用太多分,这个问题过几天就结束,没几分,请大家谅解吧!
你千万别生气啊![:)]
 
我也不在乎分啊,只是想让你知道我对这个问题的看法而已[:D]
 
gxcooo大哥:
谢谢啦![:)]
 
发分了,分数太少,大家谅解![:(]
但即便这样,我也不会平分的!
谢谢大家啦!!
 
回复收到了吗? 我发信好像有问题。 保险起见,在这里再贴一遍:

首先需要说明一点:Delphi中的String其实也是指针,只不过它是一个比较特殊的指
针,就是编译器对它自动增加了许多代码管理它的申请、释放、复制内存的操作。 它
指向的是一块通过AllocMem分配的内存首地址+8的位置,这块内存的长度是你具体字
符串的实际长度+9。和String类似的是动态数组。
对PChar,其实它和其他指针没有什么区别,首先它是一个指针,因此它可以指向任何
内存地址,而这个地址即可以是已经被别人分配的,也可以是自己事先申请好的,甚至
可以指向一个非法地址。 为了了c语法兼容,Delphi编译器对PChar指针添加了一些判
断(主要在和String进行转换时才会有用),而它经常(但不是必须)被用于指向一
个#0结尾的字符数组。

了解了上述概念,下面来解释你的疑问:

> >> strTmp := 'I am a Chinese';
注意:strTmp实际是个指针, 上述赋值语句只不过把常量字符串指针'I am a
Chinese'赋值给了StrTmp. 而指针赋值与整数赋值并没有本质区别(编译器唯一多做
的就是在给变量赋值后添加了一行汇编代码:CALL @LStrAsg, 这句的作用就是增加字
符串的引用计数)。因此上述语句相当于将一个整数赋值给另一个整数。

同理: pstr := PChar(strTmp)
之后pstr也指向常量字符串

>> > strTmp := '';
这句比较特殊, 也是delphi字符串处理与其他指针处理的最大区别.
了解了这句做了些什么,那么你的疑问也就迎刃而解了.
1. 更改strTmp原来指向字符串的引用计数, 如果这个引用计数>0则减一, 减完后如果
为0则释放原来字符串的内存. 而所有常量字符串的引用计数都是-1(不会被误释放,
其实是不会误产生共享冲突, 因为常量字符串是在代码段的,程序只能读不能写).
2. 如果赋得值为另一个字符串,那么又调用CALL @LStrAsg修改那个串的引用计数(对
常量不会改变), 然后将那个串的地址填入strTmp里, 而现在赋的是个空串, 所以
strTmp被填入了一个0.

看到了吧, 此时PStr仍然指向原来那个常量字符串, Caption := PStr 只不过是将类
内的FCaption这个字符串也指向了常量字符串而已. 所以这段代码和下面这几行代码
本质上是一样的:
var
strTmp, PStr, Caption, ConstStr: Integer;
begin
strTmp := ConstStr;
PStr := strTmp;
strTmp := 0;
Caption := PStr;
end;

有关问题[1]:
> pstr := 'wo shi xiaobenben';
> GetMem(dpstr, 100);
> dpstr := 'ni shi Another_eyes';
错误!!!! dpstr原来指向的内存没有被释放, 而dpstr现在又指向了常量字符串,你
应当时刻记住pchar只是一个普通指针,delphi对它并不像对string那样会自动管理
它指向的内存!!!

> FreeMem(dpstr);
错误!!!这句将产生一个异常,因为你打算释放常量字符串,而那里的内存你是不可
写的


关于问题[2],这只是编译器作的一点容错处理。严格来说,MyRec^.s是不对的,^操
作符表示前面那个变量是指针,它指向另一块内存,它本身也是一块内存,这个内存是
4字节长,它的内容是具体数据的起始地址。而MyRec.i中的MyRec本身就是整个结构的
首地址,两者的区别是MyRec这个变量自身是否占用另外的内存并和具体数据所在内存
不在一起。

至于参数传递,所谓传值调用,指的是传入的参数在过程中被修改的话不会影响调用前
原先的值,而传指针调用则在过程中修改传入的参数内容会直接改变调用者传入前的变
量内容。

我们可以换种方式来理解:
在Delphi中参数传递只有传地址和传具体数值两种,当是内容长度小于等于一个整数
的长度(4字节)并且参数不含var修饰词时传的是具体的数值,其他情况下传的都是
具体内容所在的地址。编译器会根据参数修改是否会影响外界自动添加一些代码。对内
部改变不会影响外界的,编译器会添加指令将传入地址指向的具体变量内容复制到栈中
(如果代码中有修改内容的语句)然后操作的都是这个副本,或者如string和动态数
组等在写前复制副本。 如果这样理解,争论是传值还是传指针就没有意义了。 我们只
要关心是否有var修饰词以了解是否允许在过程中修改传入的参数。 以move为例:
move的参数定义很有意思,它是无类型参数,对于无类型参数,编译器会将你写的变
量的地址传入。因此如果是:
var
s1, s2: string;

move(s1, s2, len);
将传入s1和s2这两个变量所在的地址(注意一点,s1和s2都是指针,具体的字符串内
容并不在它们里面),move所进行的操作就是从传入地址1处开始的内存写len个字节
到传入地址2开始的内存处,因此上面的写法显然是错误的。
而move(s1[1], s2[1], len);则是将字符串第一个字符所在的地址传入move,这才是
正确的


----- 原邮件 -----
从: 小笨笨 <taibenle@163.com>
日期: 星期一, 4月 21日, 2003 上午4:57
主题: 小笨苯向Another_eyes大侠请教,谢谢!

> Another_eyes大侠:
> 你好!久仰大名!今天耽误你一点时间,给我讲解一下有关指针的问题,谢谢!
> 下面,我就一边陈述问题,一边提问。其间,可能有偏颇、错误的地方,还请多多
指正。
>
> -------------------------------------------------------------------
> --------------
> 请看我的帖子http://www.delphibbs.com/delphibbs/dispq.asp?
lid=1583429,我把问题
> 原文拷贝过来,
> >>书上说,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;
> 其实,我至今对他们的解答都不满意!认为都没有说到关键的地方。
> [问题1]
> 下面是我后来又考虑了几天得出的结论。
> var
> pstr, dpstr: PChar;
> begin
> pstr := 'wo shi xiaobenben';
> GetMem(dpstr, 100);
> dpstr := 'ni shi Another_eyes';
> FreeMem(dpstr);
> end;
> pstr并不是没有分配内存,而是分配在进程的栈中,由于栈是被系统自动管理的,
所以,才
> 不需要我们手工地分配和释放;而dpstr是在堆中分配了内存的,所以,我们必须手
工释放。
> 大侠,你说我这样理解,对吗?
>
> -------------------------------------------------------------------
> --------------
> 请看下面的代码:
> type
> TRec = record
> i: Integer;
> s: string[10];
> end;
> ......
> var
> MyRec: TRec;
> begin
> MyRec.i := 99;
> MyRec^.s := 'aaaaa';
> end;
> 上面的代码中,MyRec.i和MyRec^.s都是正确的!!这使我很困惑,有些朋友跟我
说,是Delphi
> 自己做的简化,原本应该是MyRec^.s的,但书写起来太麻烦,比如:Edit1.Text就
没写成
> Edit1^.Text,都是一回事。
> [问题2]
> 请问大侠,他们说得对吗?
>
> -------------------------------------------------------------------
> --------------
> http://www.delphibbs.com/delphibbs/dispq.asp?lid=687168这个帖子,请看
SS2000和creation-zy
> 关于Move()到底是传值还是传指针的讨论。这是我最为糊涂的地方,我个人认为
SS2000说得
> 有一定道理。
> [问题3]
> 请大侠,解释一下到底是怎么回事?
>
> 上面3个问题,麻烦大侠给解释一下,谢谢!
>
> 祝 身体健康 生活幸福
>
> 小笨苯 2003-4-
21
>
>


 

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
顶部