我是Delphi语言的一个新手,最近在使用Pascal语言上遇到点困惑,请高人指教。
在代码中定义一个函数,返回局部String变量对应的字符串地址:
function RetChar: PChar;
var
  str: String;
begin
  str := 'Hello, world!';
  result := PChar(str);
end;然后在Button的OnClick事件中调用:
procedure TForm1.Button13Click(Sender: TObject);
var
  pStr:PChar;
  tmp:String;
begin
  pStr := RetChar;
  tmp := pStr ;
  ShowMessage(tmp);
end;
竟然运行正常,按理说返回局部String变量的字符串指针后,String变量的引用计数变为0,String变量内部字符串所占内存会被回收,为什么这样竟然能运行成功呢?

解决方案 »

  1.   


    我还做过这样的实验:
    procedure TForm1.Button13Click(Sender: TObject);
    var
      pStr:PChar;
      tmp:String;
      arr:Array[0..100000] of TStringList;
    begin
      pStr := RetChar;  for i:=0 to 99999 do
      begin
        arr[i] := TStringList.Create;
        arr[i].Add('hello,world');
      end;  tmp := pStr ;
      ShowMessage(tmp);
    end;这次动态申请的内存应该够大了,程序依然能够运行成功。
      

  2.   

    函数改为:
    function RetChar: PChar;
    var
      str: String;
    begin
      str := intostr(random(1000))+'Hello, world!';
      result := PChar(str);
    end;
    输出前多执行一次这个函数,试一试
      

  3.   

    function RetChar: PChar;
    var
      str: String;
    begin
      str := intostr(random(1000))+'Hello, world!';
      result := PChar(str);
    end;
    这样输出很不安全
      

  4.   

    我想,Delphi 编译时已经处理好这种返回方式:自动把字符串引用计数器加一。这样,函数返回时,字符串内存不会被释放。如果你不把此函数返回值赋给某个变量,编译器会发现它没有任何引用,应该还是会释放这段内存。Delphi 似乎更新版本时总在优化字符串处理方式,特别是 Pascal 字符串和 C 字符串的相互赋值,以保持 Pascal 安全稳定的特色。
      

  5.   

    正解在此:
    function RetChar: PChar;
    var
      str: String;
    begin
      //下面这行根本不是变量,引号中“写死的”是常量,亲
      //'Hello, world!'不是动态产生的,而是在编译时就已经确定了
      //下面只是把str指向了这个常量,亲
      str := 'Hello, world!';
      result := PChar(str);
    end;你想要的代码在此:
    function RetChar: PChar;
    var
      Str,S1,S2: String;
      PI:PInteger;
    begin
      SetLength(Str,3);
      Str[1]:='a';
      Str[2]:='b';
      Str[3]:='c';
      S1:=Str;
      S2:=Str;
      Pointer(PI):=PChar(Str);
      Dec(PI);
      ShowMessage(Format('常量的长度为:%d',[PI^]));
      Dec(PI);
      ShowMessage(Format('常量的引用计数为:%d',[PI^]));
      Result:=PChar(Str);
    end;函数结束后,为了效率PChar(Str)这个地址只是被标记为可用;
    类型于硬盘删除文件,只是标记此段空间可用,
    而不会执行FillMemory();将此段内存清空,这会影响效率。
      

  6.   


      //上面应该改为:
      ShowMessage(Format('长度为:%d',[PI^]));
      Dec(PI);
      ShowMessage(Format('引用计数为:%d',[PI^]));“类型于”打错,应该是“类似于”
      

  7.   

    function RetChar: PChar;
    var
      Str,S1,S2: String;
      PI:PInteger;
    begin
      str:='Hello, world!';
      S1:=Str;
      S2:=Str;
      Pointer(PI):=PChar(Str);
      Dec(PI);
      ShowMessage(Format('常量的长度为:%d',[PI^]));
      Dec(PI);
      //注意:常量的引用计数为-1
      ShowMessage(Format('常量的引用计数为:%d',[PI^]));
      Result:=PChar(Str);
    end;
      

  8.   


    正解,测试发现通过SetLeng(str,3),然后逐个字符赋值后,RetChar返回后,申请一大段内存,然后测试返回的字符串为空,应该是被FillMemory了,结贴...
      

  9.   

      //下面这行根本不是变量,引号中“写死的”是常量,亲
      //'Hello, world!'不是动态产生的,而是在编译时就已经确定了
    常量在编译时就已经确定,而且生命期与进程相同,要在进程结束时才会被释放。