因为需要Pchar作为返回值,所以把一个函数f1封装成f2,如下。完全确定f1的返回值是正确的,调用任何多次不会出现问题。但是连续多次调用f2,会在部分次数里出现返回值错误,继续调用多次,程序会报错。
function f1():TStringList;
begin
......
end;
function f2(var OutPut:Pchar):integer;
var StrL:TStringList;
begin
StrL:=f1();
         Result:=StrL.Strings[0];
OutPut:=Pchar(StrL.Text);
end;
报错情况:
Project xxxx raised exception class EAccessViolation with message ' Access violation at address 0040446f in module 'xxxx'.Read of address oode8484'.Process stopped.  Use Step or Run to continue.问题出在Pchar()这里。有谁碰到过类似的情况?原因是什么?是否Pchar指向的地址在f2结束的时候就已经无效了?如何解决?

解决方案 »

  1.   

    刚才发帖子时,比我出错的程序,在f2里少写了两行,更正一下。继续期待您的答复。
    function f2(var OutPut:Pchar):integer;
    var StrL:TStringList;
    begin
    StrL:=TStringList.Create;
             StrL:=f1();
             Result:=StrL.Strings[0];
    OutPut:=Pchar(StrL.Text);
             StrL.Destroy;
    end;
      

  2.   

    建议这样写:function f1(var fResult:TStringList):Boolean;
    begin
    ......
    end;
    function f2(var OutPut:Pchar):integer;
    var StrL:TStringList;
    begin
    StrL:=TStringList.Create;
             if not f1(StrL) then
             begin
                 showmessage('result error');
                 result := -1;
                 exit;
             end;
             Result:=StrL.Strings[0];
    OutPut:=Pchar(StrL.Text);
             StrL.Destroy;
    end;
      

  3.   

    对不起,问题不在f1的返回值。f1没有任何问题,能保证任意次调用f1是正确的,而且实际请情况里,我对f1做了返回值的判断处理。我在这里只是写了最简单的代码。问题是在连续多次调用f2的时候,OutPut:=Pchar(StrL.Text)这行代码会出错。原因是什么?是否Pchar指向的地址在f2结束的时候就已经无效了?如何解决?
      

  4.   

    按照出现问题的实际情况,做一下补充,代码在下边。排除f1出问题的可能性。问题就出现在Pchar处,连续多次执行才会出错,封装到Dll并调用的时候 ,连续调用46次时返回值开始出现错误,52次程序出错;不封装到DLL,调用f2,5次开始出现返回值错误,63次程序报错。是否Pchar指向的地址在f2结束的时候就已经无效了?Pchar指针有什么特殊的地方?function f1(PageStr:String):TStringList;
    begin
    //Result.String[0]在-1的时候表示result error
    //f1做测试,不存在问题
          ......
    end;
    function f2(Str:Pchar;var OutPut:Pchar):integer;
    var StrL:TStringList;
    begin
    //Result在-1的时候表示result error
    StrL:=TStringList.Create;
    StrL:=f1(StrPas(Str));
    Result:=StrL.Strings[0];
    OutPut:=Pchar(StrL.Text);
    StrL.Destroy;end;
      

  5.   

    统计还挺全的,不过不知道你的f2函数的语句Result := StrL.Strings[0];是什么意思,
    也不知道是怎样通过编译的。如果StrL没有内容,就会发生ListIndexOutOfRange异常。代码很混乱。你的f2之所以在返回时得到一个无效的Output是因为,PChar转换是不会增加字符串的引用计数的,f2返回的时候Output指向的字符串已经在end语句之前释放了。如果f2的Output声明为string类型相对来说比较方便,否则你要在f2函数体内在堆中分配存放Output的内容的空间,然后在f2函数体外释放它,如果扯到DLL,那就更麻烦了。按照pazze大佬的一贯说法,换个思路。
      

  6.   

    你的OutPut分配内存了么???
      

  7.   

    dbExpress(大虾挥挥手,说道:“小菜鸟,不是这样的),麻烦您看明白了再说成吗?StrL是个TStringList的,它是由f1赋值的;我多次强调过f1的返回值也是TStringList的,而且f1的返回值没有问题,不会为空,不会出现ListIndexOutOfRange异常;Result := StrL.Strings[0];的意思是说f2的返回值是StrL的第一个元素;OutPut无效?我说过OutPut无效吗?请仔细看一下f2的声明,function f2(Str:Pchar;var OutPut:Pchar):integer;其中var OutPut:Pchar ,谁告诉你我没给它分配空间,谁告诉你Output指向的字符串已经在end语句之前释放了?我需要把这些封装到DLL供其他语言使用,不能String而用Pchar,指针和变量,您分的清吗?麻烦您再好好考虑考虑?您的时间宝贵,我的时间也宝贵。
      

  8.   

    麻烦dbExpress去看一些关于 指针,变量,别名 的东西。很感谢你说的换个思路,这句话有用。
      

  9.   

    建议搂主写程序不要这么写。不要直接调用Destroy方法,最好调用Free;还有给一个对象变量赋值后在没有释放时,不要再赋另一个值。这样会造成内存泄漏。还有PChar是一个指针变量,使用前要分配内存,使用后要释放内存。function f1(PageStr:String):TStringList;
    begin
    //Result.String[0]在-1的时候表示result error
    //f1做测试,不存在问题
          ......
    end;
    function f2(Str, OutPut:Pchar):integer;
    var StrL:TStringList;
    begin
    //Result在-1的时候表示result error
    //StrL:=TStringList.Create;
    StrL:=f1(StrPas(Str));
    Result:=StrL.Strings[0];
    //OutPut:=Pchar(StrL.Text);
             StrPCopy(OutPut, StrL.Text); 
    //StrL.Destroy;
             StrL.Free;
    end;在调用f2之前对应OutPut参数PChar变量要先分配内存。记得使用完后释放。
      

  10.   

    拜托各位,有谁能看明白我关于f2的函数声明?
    function f2(Str:Pchar;var OutPut:Pchar):integer;

    function f2(Str:Pchar;OutPut:Pchar):integer;
    差着一个Var呢!看明白再说我没分配空间成吗?
      

  11.   

    OutPut其实也是f2的一个返回值,是其他地方调用f2时候传入参数的一个别名,多少懂一点面相对象的人大概都会这么用吧?别名,指针,变量,很简单的一个用法,怎么就还没看明白?
      

  12.   

    Drate(鸟窝里的虫) ,您再和我一起想想?目前为止,只有您看明白了 var
      

  13.   

    哎,看来你真的不明白我为什么要把function f2(Str:Pchar;var OutPut:Pchar):integer;改
    成function f2(Str, OutPut:Pchar):integer;这样因为可以使分配空间和释放空间在同一个函数内进行。这是一种好的编程规范。对于PChar变量指针。指针变量它的值是地址,对于这种变量不需要使用Var,除非你想在调用函数内给PChar变量分配地址。
      

  14.   

    好的编程规范?你哪个老师这样教你的?你老师就没告诉说用这个规范,一个函数只能有一个返回值?是不是想要多个返回值就只能用全局变量了?你用过引用吗?规范也是你定的了的?f2是用来把f1封装到DLL的,DLL的接口都定了,允许你随便改动?那你再给做一层封装?你以为这是你过家家呢?如果就给你这么个接口,你就得这么实现,你以为是你老师给你布置作业呢?你老师哪儿毕业的?
      

  15.   

    前提:每次调用时的实参完全相同。使用封装的DLL,调用f2,连续调用46次时返回值开始出现返回值不在预料之中,前45次都是正确的,52次程序出错;不封装到DLL,调用f2,5次开始出现返回值返回值不在预料之中,前4次是正确的,63次程序报错。不做封装也不调用f2,只调用f1,不会有任何返回值不在预料之中。结论:f1没有问题,唯一合理的解释 Pchar(StrL.Text)把数据保存到某地址,并返回首地址之后,在退出f2之后,Delphi 不再保证那段地址的内存不可被使用。此时如果有声明变量或者申请内存空间的时候,发生冲突,就会使记录在那段地址的返回值长度发生变化,出现在我程序里的现象就是返回值在调用多次后,字符串 突然变短或者变长,再经过多次调用之后,首地址被再次申请利用,导致程序报错。才出现我最初提的问题。
      

  16.   

    建议先给OUTPUT赋初值,保证长度足够,再试一下吧。
      

  17.   

    你F2函数的返回值是什么类型?
    你声明的integer,却在过程中给他字符型。
      

  18.   

    谢谢,但是应该和长度没关系,前45次的结果都是正确的。而且长度不经过调用f1也确定不了。如果真的是不能保证那段地址不会被再次使用,长度再长,也没用;不过我也会去试试。C的已经实现这部分,还要把其他很多东西都改成C的,有够累,去做了。Delphi中AllocMem也不成,下一步考虑用动态数组实现,我想不可能什么办法都没有,TStringList或者TStrings或者String 转成Pchar并且用引用这种方式作为返回值,一定可以实现,Delphi不应该不能封装这样的DLL。
      

  19.   

    kingecg(山德鲁) !!!您说的对,不过我在自己的代码里写的是Result:=StrToInt(StrL.strings[0]);这帖子里写错了。谢谢您仔细的看过我的代码并指出这个错误。希望您能继续帮我考虑考虑。
      

  20.   

    你的F1的返回值是什么情况。
    下面是我测试的结果:
    代码:
    ******
    function TForm1.f1: tstringlist;
    begin
    result:=tstringlist.Create;
    result.Add('2222222');
    result.Add('ssss');
    end;function TForm1.f2(var output:pchar):integer;
    var StrL:TStringList;
    begin
    //Result在-1的时候表示result error
    StrL:=TStringList.Create;
    StrL:=f1;
    Result:=strtoint(StrL.Strings[0]);
    OutPut:=Pchar(StrL.Text);
    StrL.Destroy;end;procedure TForm1.Button1Click(Sender: TObject);
    var st:pchar;
    i:integer;
    begin
    for i:=0 to 50 do
    f2(st);
    end;
    *********
    结果:50遍没有错误怪现象?????
      

  21.   

    to kingecg(山德鲁):
    1. 你的f2其实调用了两次TStringList.Create,而只调用了一次TStringList.Destroy;
    2. 你在Button1Click中光调用f2而不存取st当然看不到异常啦。to musicdog(白狮子):
    你别扁我!就当我没说过。
      

  22.   

    我的是一个多种书籍页码编码的打印字符串的运算,比如输入 'i-vi,一-十一',最终要求OutPut 为 
    '17
    i
    ii
    iii
    iv
    v
    vi










    十一
    '
    开始40多次都对,后来有的时候长(加了多余的字符),有的时候短。
    你把你的的output输出看一下,每次都对吗?我也去试试。
      

  23.   

    不对,我在button1click的循环中加showmessage(st);
    3遍以后的结果是
    **** 第4遍*****
    222222
    ssss****第5遍******
    222222
    ssss少了一个???
    但在内存中strl.text的值是'2222222'#$D#$A'ssss'#$D#$A
    还是4个s!!!!!!!!!!!
    困惑中......
      

  24.   

    to dbExpress,关于你指出的kingecg(山德鲁)的问题,说的不对。他的Create和Destroy没有错。一个函数自身的返回值Result如果是TStringList,不能Destroy,如果Destroy哪还有返回值,但是Create必须有,没有就报错了。
      

  25.   

    kingecg(山德鲁),你的问题和我的一样,我困惑的就是这里,终于有一个人可以明白我的话了。
      

  26.   

    我同意使用StrPCopy
    另外我也同意: zuoyexingchen(昨夜星尘)的看法,Pchar类型,无所谓Var的,都是地址,本也是一直这么的用过来的,而且是在Dll中最常用到。
      

  27.   

    to czx0514(czx0514) ,你在说笑话?你去试试,你试过没有?没试过就别发言
      

  28.   

    to : musicdog(白狮子) 你自己试过了吗?不要动不动就发脾气,这样不是很无聊吗?
    凭经验你差远了,大家的已经都是凭自己的积累而论的,你不但没有把自己的问题讨论好,反而让人觉得很没有意思。
      

  29.   

    OutPut:=Pchar(StrL.Text);
    =>
    getmem(output,length(strl.text);
    strpcopy(output,strl.text);
      

  30.   

    对于PCHAR的类型,要不要使用一下 New() 呀?
      

  31.   

    to czx0514(czx0514) 评经验,你个小菜鸟才敢说别人没经验。zuoyexingchen(昨夜星尘)的代码我上午就试了。你闭嘴吧
      

  32.   

    to VCBoyGirl(明子)  应该不用,你看kingecg(山德鲁)的代码,前几遍也没有错。
      

  33.   

    to  xiaocha(小查)  我正在试
      

  34.   

    function F1() : TStringList;
    var
      PList : TStringList;
    begin
      PList := TStringList.Create;
      try
        PList.Add('123456');
        PList.Add('ABCDEF');
        Result := PList;
      finally
        //PList.Free;
      end;
    end;function F2(output : pchar) : Integer;
    var StrL:TStringList;
    begin
      //Result := -1;
      StrL:=TStringList.Create;
      try
        StrL:=F1();
        Result:=StrToInt(StrL.Strings[0]);
        StrPCopy(OutPut, StrL.Text);
      finally
        StrL.Free;
      end;
    end;
    procedure TForm1.HsBitBtn1Click(Sender: TObject);
    var
      lRel : integer;
      sOutBuffer : string;
    begin
      SetLength(sOutBuffer, 100);
      lRel := F2(PChar(sOutBuffer));
      SetLength(sOutBuffer, StrLen(PChar(sOutBuffer)));
      Memo1.Lines.Add(IntToStr(lRel) + sOutBuffer);
    end;到底谁菜,自己明白了吧!无聊!可以试吗?你真的很有个性,无聊分子。
      

  35.   

    注意:使用完output后,freemem(output);
      

  36.   

    to  czx0514(czx0514) ,你真是没个性。StrPcopy 前没有 getmem 也能成功的吗?怎么有脸说?
      

  37.   

    to xiaocha(小查) 成功!感谢你!一并感谢  kingecg(山德鲁)  等亲自动手试过的人。不好意思,没想到这么简单就能解决的问题,招来这么大风波,最初定的分数显得很不够了。
    但是我想那些愿意亲自动手尝试的人们不完全是看分数来的。xiaocha(小查)如果愿意,请留下QQ号,Delphi还有好多问题要请教。再次感谢。结帖去。
      

  38.   

    在OutPut:=pchar(StrL.text);前加getmem(output,length(strl.text));可以
    但是,我发现没办法free得到到内存,不知道会不会造成内存泄漏
      

  39.   

    to  czx0514(czx0514) ,你真是没个性。StrPcopy 前没有 getmem 也能成功的吗?怎么有脸说?丫还在这里唧唧歪歪
      

  40.   

    to kingecg(山德鲁) 忙的有点晕,结帖的时候分了80和20,找你的帖子,不小心找错了。100分全被小查霸占了。呵呵,不好意思。也留一个你的信箱好吗?以后也有很多问题请教。