关于强制类型转换后的内存问题,我想知道是否需要释放?
如:
function ShortStringAsPChar(S:ShortString):PChar;
var s1:string;
begin
  s1:=s;     //将shortstring转换为string
  Result:=pchar(s1); {返回PChar化的字符串}
end ;
string类型是生存期自管理的,而pchar不是,那么将string强制转换为pchar类型后是否还需将转换后的变量进行释放?另外看一下这个函数定义的可以吗?
《Delphi5 开发人员指南》上说:
“在练习将一个字符串转换为PChar类型时要小心,因为字符串在超出其作用范围时有自动回收的功能,因此当进行P:=PChar(Str)的赋值时,P的作用域(生存期)应当大于Str的作用域。”
这句话是什么意思?好像是说反了吧?

解决方案 »

  1.   

    没有说反,也就是说P的长度要大于Str的长度,否则当Pchar把str转换的时候由于P值得到的长度大于本身定义的长度就会出现问题,所以在使用的时候注意下就可以了。你要知道pchar类型和string类型的最大区别就是正常字符串的末尾加入了一个空字符来表示字符的结束。你可以通过诸如WinHex之类的软件来观察!
      

  2.   

    好像不用释放的吧, 至少我从没见过有人释放Pchar...
      

  3.   

    好像不会释放procedure TForm1.Button1Click(Sender: TObject);
    var s:string;
      p:pchar;
    begin
      s:='i love you';
      p:=pchar(s);
      messagebox(handle,p,'',mb_ok);
      memo1.Lines.Add('s address:'+inttostr(integer(s)));
      memo1.Lines.Add('s[1] address:'+inttostr(integer(@s[1])));
      memo1.Lines.Add('s[2] address:'+inttostr(integer(@s[2])));
      memo1.Lines.Add('s[3] address:'+inttostr(integer(@s[3])));
      memo1.Lines.Add('p address:'+inttostr(integer(p)));
      s[1]:='u';
      messagebox(handle,p,'',mb_ok);
      memo1.Lines.Add('s address:'+inttostr(integer(s)));
      memo1.Lines.Add('s[1] address:'+inttostr(integer(@s[1])));
      memo1.Lines.Add('s[2] address:'+inttostr(integer(@s[2])));
      memo1.Lines.Add('s[3] address:'+inttostr(integer(@s[3])));
      memo1.Lines.Add('p address:'+inttostr(integer(p)));end;procedure TForm1.Button2Click(Sender: TObject);
    var
      st:string;
      pt:pchar;
    begin
      pt:=pchar(strtoint(edit1.Text));
      messagebox(handle,pt,'',mb_ok);
    end;procedure TForm1.Button3Click(Sender: TObject);
    begin
      showmessage(chr(pbyte(strtoint(edit1.Text))^));
    end;经过我观察,点击按钮button1后,把p的地址写入edit1中点击button2 还能看到'i love you',说明这些字符还在内存中。
      

  4.   

    应该不用释放吧~~~~pChar强制类型转换只是得到了string类型的数据字段指针。而string则是自管理的。不过我觉得你p:=PChar(s);之后,不要修改s的内容了(如果你还要用p的话),因为s会重新分配内存,导致string类型数据字段指针改变,而p所指向的内容还是原来的个人见解
      

  5.   

    楼上的,我认为p:=PChar(s);之后,修改s的内容不会改变p所指向的内容,因为s的值应该是被复制到新的内存中了。对吗?
      

  6.   

    我看才看了看,的确如此,如果修改字符串类型s的值,实际上是释放掉原来的内存并重新分配了内存。强制类型转换实际上在内存中将s复制了一份,而DELPHI应该有垃圾收集机制,所以这样的内存应该没有必要释放,因为调用FreeMem的时候,反而出现了异常.
    代码如下:
    var p:PChar;s:string;
    begin
        s:='abcd';
        p:=pChar(s);
        Memo1.Lines.Add('Initialized s.');
        Memo1.Lines.Add(Format('s'' pointer:%p',[@s[1]]));
        Memo1.Lines.Add(Format('p'' pointer:%p',[Pointer(p)]));
        MessageBox(handle,p,nil,MB_OK);
        Memo1.Lines.Add('s changed.');
        s:='cd';
        Memo1.Lines.Add(Format('s'' pointer:%p',[@s[1]]));
        Memo1.Lines.Add(Format('p'' pointer:%p',[Pointer(p)]));
        //这里,p的指针和前面的是一样的,而s的指针却改变了.
        MessageBox(handle,p,nil,MB_OK);
        Memo1.Lines.Add('one byte of s changed.');
        s[1]:='d';
        Memo1.Lines.Add(Format('s'' pointer:%p',[@s[1]]));
        Memo1.Lines.Add(Format('p'' pointer:%p',[Pointer(p)]));
        //这里,p和s的指针都没有改变
        MessageBox(handle,p,nil,MB_OK);
        FreeMem(p);//产生了一个Invalid Pointer Operation的异常
      

  7.   

    不用释放,您在强制转换后,PChar(s)只是得到了s的地址,
      

  8.   

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★【我是楼主】to: SmallHand(火龍) 
    我想你理解错了。《Delphi5 开发人员指南》上说的是P的作用域(生存期)应当大于Str的作用域而不是字符串长度的问题。我觉得《Delphi5 开发人员指南》上的那句话还是有问题。to: Eastunfail(恶鱼杀手) 
    我同意“如果修改字符串类型s的值,实际上是释放掉原来的内存并重新分配了内存。”这句话,但是,在你的例子里当p:=pChar(s);后,执行s:='cd';时,我想并没有释放掉原来的内存,因为在p:=pChar(s)时增加了一次对s的引用,s:='cd时只是新开辟了一块内存并将'cd'写入,而没有释放'abcd'所在的内存,如果在s:='cd'前s的引用计数为1那么它才会被释放。我不知道我这么想是否正确,主要问题是在p:=pChar(s)是否会增加对s的引用次数?请给予我明确的答复,谢谢!
    http://expert.csdn.net/Expert/topic/1798/1798183.xml?temp=4.923648E-02
    这个问题你也回答过。也请大家看一下。总的来说,我可以使用指针什么的,但是对Delphi的内存管理方法还是糊里糊涂,比如说,new() dispose() getmem() freemem()等等我知道怎样用,但是我不知道它们都具体干了些什么,它们是怎样操作内存的。★★下面是我猜想的,请大家看看对不对???
    我们知道,Delphi的内存分配方式与VC不同,Delphi的动态变量使用的内存是分配在堆中的,
    var i: ^integer;
    begin
      new(i);
      i^ := 5;
      dispose(i);
    end;
    在new(i); i^ := 5;之后,i中保存的是5所在的那块内存的起始地址,5被分配在堆中并占据4个字节的内存空间。
    dispose(i);之后,都干了些什么?我不知道。
    我猜想:在dispose(i);之前程序是怎样知道5所在的那块内存是不能被分配的?是不是5所在的那块内存中有什么标记表明它正在被其他指针使用?
    我猜想:dispose(i);之后,5仍然在那块内存中,只是标记被更改,表明这块内存没有被其他指针使用可以被再分配。而i中的地址值是不会变化的。
    i所在的内存的地址应该是固定的,即使是对i重新赋值。大家可以用IntToStr(integer(@i))来检查一下。这一点与string类型的变量不一样,也许是因为string类型是生存期自管理的吧!我不明白?请各位老师确定或否定我的猜想,谢谢了!!!!!★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
    ---------------------------------------------------------
      

  9.   

    我觉得书上说的正确。因为在Delphi中string类型是生命期自动管理的,而它的这种自动管理是基于对该string类型变量的引用计数。所以如果当强制转换后的pchar类型的数据的作用范围如果小于原始的string类型的变量,那么有可能产生的一个副作用就是,因为对一个string进行强制转换时,delphi并没有相应增加对原始string的引用计数。由此导致的直接后果就是当delphi检测到对string的引用数降至零时,它就收回了string的内存。但是我们还有一个经强制转换后得到的pchar类型的变量,这样一来当然极可能产生问题。
      这样的理解不知对是不对?
      

  10.   

    对不起我发现了我观点中的错误。
      当对string类型变量进行强制类型转换时,delphi同样会增加对它的引用计数,即经转换后得到pchar也拥有一个对原始string的引用计数。因为有这个引用对数的存在,所以当pchar的作用范围大于string时,它会阻止delphi提前释放string的内存。
      

  11.   

    呵呵,讨论好热烈,好久没有见到这么HOT的帖子了,到了公司加入你们的讨论!!!!
      

  12.   

    dispose(i);之后,5仍然在那块内存中,只是标记被更改,表明这块内存没有被其他指针使用可以被再分配。而i中的地址值是不会变化的。
    ----------------------------------------------------------------------------------
    dispose(i);之后,5已经不在那块内存中
    看一段代码就知道了
    var i: ^integer;
      addr:integer;
    begin
      new(i);
      i^ := 5;
      addr:=integer(i);
      memo1.Lines.Add('i 指向的地址  '+inttostr(integer(i)));
      memo1.Lines.Add('addre 的值  '+inttostr(addr));
      memo1.Lines.Add('addre 指向的值    '+inttostr(pinteger(addr)^));
      dispose(i);
      memo1.Lines.Add(' ');
      memo1.Lines.Add('dispose 之后');
      memo1.Lines.Add('i 指向的地址  '+inttostr(integer(i)));
      memo1.Lines.Add('addr 指向的值    '+inttostr(pinteger(addr)^));  i:=nil;  memo1.Lines.Add(' ');
      memo1.Lines.Add('nil 之后');
      memo1.Lines.Add('i 指向的地址  '+inttostr(integer(i)));
      memo1.Lines.Add('addr 指向的值    '+inttostr(pinteger(addr)^));end;
      

  13.   

    【我是楼主】
    --------------------------------------------------------------------------
    谢谢大家的关注,可是还是没有人能全面清楚的进行解释!!!!!!!!!
    难道就要这样糊里糊涂下去吗???
    只是会用,却不知道为什么这样???我不想这样下去!!!!!!!
    这么多的人里就没有一个懂的吗???????
    --------------------------------------------------------------------------
    to:TreeLand() 《Delphi5 开发人员指南》上说:
    “在练习将一个字符串转换为PChar类型时要小心,因为字符串在超出其作用范围时有自动回收的功能,”
    从上面这句话理解,一个字符串转换为PChar类型时应该是不增加字符串的引用记数的,我也说不准,没看到哪本书上有明确的说明,哎!!!!
    --------------------------------------------------------------------------
    对string类型进行pchar类型的强制转换到底增不增加string类型变量的引用记数???我不知道?但是很关键。
    --------------------------------------------------------------------------
    to:itytramper(阿琪) 你的例子我还没有来得及上机实验,我只是看了看。
    你的例子中 i指向的地址 和 addre的值 应该是一个值吧,如果想要取i的地址(不是i中保存的地址)应该用 integer(@i)吧?我不确定!等我上机实验一下再说。
    --------------------------------------------------------------------------
    to:Eastunfail(恶鱼杀手) ( ) 信誉:78  2003-06-09 19:57:00  懂就说出来嘛,别买关子了!说这么一句就走了啊!!不能放过你!!!
    --------------------------------------------------------------------------
      

  14.   

    【我是楼主】to:itytramper(阿琪) ★你看看这个例子怎么回事?
    http://expert.csdn.net/Expert/topic/1798/1798183.xml?temp=.8233911procedure TForm1.Button1Click(Sender: TObject);
    var i: ^integer;
    begin
      new(i);
      i^ := 5;
      dispose(i);
      Edit1.Text := IntToStr(i^);//显示5
      i^ := 6; // 为什么在dispose(i)之后执行它居然不会报错?
      Edit2.Text := IntToStr(i^);//显示6
    end;
    {我对指针这块真是搞不懂,为什么执行dispose(i)之后,下面的语句居然还能正常执行并显示正确的结果呢?按理说,在执行dispose(i)之后,i就应该变成值无定义了,可是为什么i^还能指向5?并且5所在的那块内存不是应该被释放了吗?
    谁能给我讲讲指针的结构。new,dispose都是怎样工作的,内存上是怎样管理的?
    谢谢了!!}
      

  15.   

    new是向操作系统申请一块内存,并返回其地址,
    dispose是告诉操作系统收回这块内存,可以另做它用。
    这两种操作本身并不改变该内存中实际内容,所以即使释放后,也可以通过地址(指针)引用该地址,如其内容未被另的程序修改,是不会改变的。
      

  16.   

    看~~~
    http://www.pcvc.net/category/content.asp?sendid=114
    http://www.pcvc.net/category/content.asp?sendid=118
      

  17.   

    这句话没有说反
    强制类型转换只是得到了string[0]指针罢了
    你这里string是临时变量 在stack中分配的内存
    所以当返回时这个字符是被清除掉了
    但是你的函数本身在调用时占用了调用者的堆内存所以
    那时DELPHI会自动把这个字符串复制到调用者的堆内存中
    所以生存期很重要 如果不是用函数本身返回
    而用参数的话就会出错
    那句话说的十分正确
      

  18.   

    win2000 delphi6我的代码的结果
    i 指向的地址  13452928
    addre 的值  13452928
    addre 指向的值    5
     
    dispose 之后
    i 指向的地址  13452928
    addr 指向的值    13452924
     
    nil 之后
    i 指向的地址  0
    addr 指向的值    13452924
    ******************************************************************
    这里i的地址是不变的,我们关心的是i里面的内容,也就是5的地址 addr ,和这个地址里的值 value
    dispose以后,addr是不变的,value变成了13452924
    只有i:=nil后addr才等于0 ***********************************************你的代码的结果
    13443508  //在我的机子上并不是5
    6          i^ := 6; // 为什么在dispose(i)之后执行它居然不会报错?
    //这样是不安全的,这个地址的内存可能已经在别的地方用了
      

  19.   

    1、当然不用释放;
    2、“因此当进行P:=PChar(Str)的赋值时,P的作用域(生存期)应当大于Str的作用域。”这话没错,这不是SmallHand(火龍)说的什么长度问题,而是,你的函数返回时,返回的指针指向的s1已经自动释放了!也就是说,这个函数只会引起程序出错!
      

  20.   

    对不起,我前面的回答是想当然的,Eastunfail(恶鱼杀手) 的回答很认真,不管是否能完全解释清楚,至少,看看他的测试是很有用的,他的态度也值得学习!
    多数人都(包括我本来)认为P=pchar(S)是对S的引用,至少,现在清楚,这是错的。Delphi是对S复制了另一份!
    另外,不管内部机制如何,释放代码确实是没必要的。
      

  21.   

    嘻嘻,前面我认为自己一开始说错才是真正的错。本来我“想当然”地认为P:=PChar(S)是对S的引用并没错。在Eastunfail(恶鱼杀手) 的代码中,由于常数的干扰,让我判断错了。如果将S:='1234'中的常量'1234'换成动态的就可看出,P的地址与S相同。至于“因此当进行P:=PChar(Str)的赋值时,P的作用域(生存期)应当大于Str的作用域。”这话,我原来没细看,那确实是说反了,即应该是“Str的作用域(生存期)应当大于P的作用域。”更严格地说,在P本次赋值的作用完成之前,除了保证Str的有效存在,还不能对Str重新赋值!运行下面的代码就可以清楚了!
    S :=  '? ' + DateTimeToStr(Now);
    P := PChar(S);
    S[1] := '!';
    ShowMessage(P);//此时显示的是S的新值!
    S :=  S + S;
    ShowMessage(P);//此时结果不可预测!现在非常清楚,PChar根本只是一个引用指针,指向的内存并不属于它,也不存在引用计数的问题,当然谈不上什么释放!
      

  22.   

    谢谢大家的关注,可是还是没有人能全面清楚的进行解释!!!!!!!!!
    难道就要这样糊里糊涂下去吗???
    只是会用,却不知道为什么这样???我不想这样下去!!!!!!!
    这么多的人里就没有一个懂的吗???????
    //说实话,我很反感楼主上面的某些言论。1+1=2 我们用了很多年,但如果有人正确的解释了为什么会是这样,我想可能不是每个人都能明白。建议楼主找一份object pascal 手册看看(置顶的那个地址我试了一下,失败),也许不能完全解答你的疑问,但至少能加深你delphi对字符串类型数据的管理方式。讨论的前提是有一定的基础设置水平相当,否则,只能叫学习.管见,仅供参考
      

  23.   

    to:Adayuer([±0℃]) 
    我知道我菜,我就是来学习的。我可没想浪费你的时间。
      

  24.   

    首先,楼主把书上的话记错了!“在练习将一个字符串转换为PChar类型时要小心,因为字符串在超出其作用范围时有自动回收的功能,因此当进行P:=PChar(Str)的赋值时,P的作用域(生存期)应当大于Str的作用域。”其实,书上说的是(我用的是Delphi6 开发人员指南,不知道5 的是不是翻译错了):“在练习将一个字符串转换为PChar类型时要小心,因为字符串在超出其作用范围时有自动回收的功能,因此当进行P:=PChar(Str)的赋值时,P的作用域(生存期)要比Str 长。”
    和作者摘的含义正好相反。这句话的意思是这样的:字符串类型是具有自动回收功能的,但是字符串指针没有。P:=PChar(Str)返回的指针可能在作用域之外使用,因此P 的生存周期可能比Str 要长。举个例子来说明:
    procedure getPChar(P: PChar); 
    var
        strTmp: string;
    begin
        strTmp := 'asasass';
        P := PChar(strTmp);
    end; 这个时候,明显返回的这个指针P 的作用域要大于strTmp,那么这个过程结束的时候,strTmp已经被自动释放掉了。调用这个过程的函数得到的P 事实上已经是一个悬挂指针,没有意义了。作者的本意是提醒读者注意防止这样的情况发生。
      

  25.   

    To:Spacesoft(暗夜狂沙)
    书上就是那么写的,我没有记错!我看的是ChinaPub出的PDF格式的《Delphi5 开发人员指南》,这里面有不少的错误(翻译上的),如:书上的原代码注释上写着:自定义光标的序号应该小于0(大概是这句话,位置我忘了。)而我看原代码时发现注释中明明写着自定义光标的序号应该大于0。我倒!
    不知道纸书上怎样?
      

  26.   

    wan_ming(wan ming):大家的回答还不够清楚么?不尊重人
      

  27.   

    引用:
    运行下面的代码就可以清楚了!
    S :=  '? ' + DateTimeToStr(Now);
    P := PChar(S);
    S[1] := '!';
    ShowMessage(P);//此时显示的是S的新值!
    S :=  S + S;
    ShowMessage(P);//此时结果不可预测!现在非常清楚,PChar根本只是一个引用指针,指向的内存并不属于它,也不存在引用计数的问题,当然谈不上什么释放!这是非常正确的,另注意DELPHI为了兼容PChar,在string的最后自动加了'\0'。p:= pchar(s)只是返回s[0]的地址,不影响引用计数器。所以如果p的生存期超过s,则p不可预测。