• pchar,pwidechar,pansichar作为返回参数时内存访问错误


    function Test:pachr;
    var
      str: string;
    begin
      str := 'Test Char';
      result:=pchar(str); 

    end;

    上面的Test函数作为导出函数时候会出现访问野指针,因为str已经被释放了;


    方法一:改成全局变量保存的话,可以正常访问到数据

    var
    DataStore:String;
    function Test:pachr;
    var
      str: string;
    begin
      str := 'Test Char';
      DataStore:=str;
      result:=pchar(DataStore); 
    end;



    方法二:给字符指针分配内存
    function Test:pachr;
    var
      str: string;
    begin
      str := 'Test Char';

      Result:=StrAlloc(Length(str)+1);

      StrPcopy(Result,str);

    end;



    以下是万一的博客中关于字符串分配内存
    万一的博客 http://www.cnblogs.com/del/archive/2008/11/08/1329543.html

    Delphi 的内存操作函数(1): 给字符指针分配内存


    马上能想到的函数有:

    GetMem
    AllocMem
    ReallocMem
    FreeMem
    
    GetMemory
    ReallocMemory
    FreeMemory
    
    New
    Dispose
    
    NewStr
    DisposeStr
    
    StrNew
    StrAlloc
    StrDispose
    
    GlobalAllocPtr
    GlobalFreePtr
    
    WideStrAlloc
    AnsiStrAlloc
    StrDispose
    
    Move
    MoveMemory
    CopyMemory
    ZeroMemory
    FillMemory
    FillChar
    
    StrBufSize


    给字符指针(PChar、PWideChar、PAnsiChar)分配内存, 最佳选择是: StrAlloc.

    StrAlloc 虽然最终也是调用了 GetMem, 但 StrAlloc 会在指针前面添加 Delphi 需要的 4 个管理字节(记录长度).

    StrAlloc 分配的内存, 用 StrDispose 释放, 用 StrBufSize 获取大小.

    用 FreeMem 释放可以吗? 这样会少释放 4 个字节.

    这种类型的指针一般用于 API 函数的参数, 譬如获取窗口标题:

    var
      p: PChar;
    begin
      p := StrAlloc(256);
      GetWindowText(Handle, p, StrBufSize(p));
      ShowMessage(p); {Form1}
      StrDispose(p);
    end;


    StrAlloc 根据不同的参数(PWideChar、PAnsiChar)分别重载调用了 WideStrAlloc、AnsiStrAlloc, 所以我们也可以直接使用这两个函数(这也需要用 StrDispose 释放), 不过使用它们的必要性不大; 用 StrAlloc 指定好参数类型即可.

    给字符指针分配内存其他方法也挺方便, 譬如:

    //获取 WINDOWS 所在目录
    var
      buf: array[0..MAX_PATH] of Char;
    begin
      GetWindowsDirectory(buf, Length(buf));
      ShowMessage(buf); {C:WINDOWS}
    end;


    数组的内存不是我们自己申请的, 系统会自动释放; 记住: 只要是手动申请的内存一定要手动释放.

    我们给字符指针申请内存主要是为了在 API 中接受数据, 如果我们要直接赋给常量值, 系统会自动分配内存的, 譬如:

    var
      p: PChar;
    begin
      p := '万一的 Delphi 博客';
      ShowMessage(p); {万一的 Delphi 博客}
    end;


    当然我们也可以用这种办法申请内存, 就是笨了点, 譬如:

    //获取系统目录
    var
      p: PChar;
    begin
      p := PChar(StringOfChar(Char(0), 256)); {反复一个空字符 256 次成一个字符串, 然后转为 PChar}
      GetSystemDirectory(p, StrBufSize(p));
      ShowMessage(p); {C:WINDOWSsystem32}
    end;


    如果在 API 函数需要的字符指针是为了输入, 当然也不需要申请内存, 譬如:

    //设置窗口标题
    var
      p: PChar;
    begin
      p := '窗口新标题';
      SetWindowText(Handle, p);
    end;
    
    //也可以直接给常量
    begin
      MessageBox(Handle, '提示信息', '标题', MB_OK);
    end;
    
    //如果是给字符串的变量或常量, 则需要转换一下
    var
      str: string;
    begin
      str := '万一的 Delphi 博客';
      TextOut(Canvas.Handle, 10, 10, PChar(str), Length(str));
      {在窗体上输出文字, 此代码不能在 OnCreate 事件中}
    end;


    跑题了...到现在已用到了 StrAlloc、StrDispose、WideStrAlloc、AnsiStrAlloc、StrBufSize 几个函数.

    还有 NewStr、DisposeStr、StrNew、StrDispose 也貌似有点关系.

    先说 NewStr 和 DisposeStr(它们是一对); 
    NewStr 是根据 AnsiString 再新建一个 PAnsiString, 不过这是为兼容而存在的, Delphi 已不提倡使用了.
    不再提倡使用的函数都缀以 deprecated 标识, 并在代码提示中用灰色显示.
    其实用 @ 即可获取字符串指针, 当然根本用不着它们.

    还有个 StrNew; StrNew 可以再制一个相同的字符指针, 譬如:

    var
      p1,p2: PChar;
    begin
      p1 := 'Delphi';
    
      p2 := StrNew(p1);
      ShowMessageFmt('%s, %s', [p1, p2]); {Delphi, Delphi}
    
      p1 := '2009';
      ShowMessageFmt('%s, %s', [p1, p2]); {2009, Delphi}
    
      StrDispose(p2); {释放自己申请的}
    end;


    不过 StrNew 存在的意义也不大, 我们可以更简单地完成上面的操作:

    var
      p1,p2: PChar;
    begin
      p1 := 'Delphi';
      p2 := p1;
      ShowMessageFmt('%s, %s', [p1, p2]); {Delphi, Delphi}
      p1 := '2009';
      ShowMessageFmt('%s, %s', [p1, p2]); {2009, Delphi}
    end;


    说来说去, 好像只有 StrAlloc 是我们值得我们记忆的?

    还有一对非常重要的相关函数: GlobalAllocPtr、GlobalFreePtr; 它们的功能是上面这些都不可替代的!

    GlobalAllocPtr 和 GlobalFreePtr 是对系统函数: GlobalAlloc、GlobalFree 的简化, 之所以说它们重要, 只是因为它们可以跨进程操作; 不过 GlobalAllocPtr 是给无类型指针(Pointer)分配内存, 当然就不仅仅用于字符指针了. 还是到后面专题再做例子吧.

    
    
  • 相关阅读:
    UniGUI 之UniDBGrid(05)
    Spring事务管理
    MySQL数据库隔离级别
    MySQL并发控制
    MySQL存储引擎
    数据库事务(2)---ACID与并发问题
    数据库事务(1)----- JDBC事务与JTA事务
    navicat Window . MAC版常用快捷键
    重要博客
    yunw
  • 原文地址:https://www.cnblogs.com/h2zZhou/p/10043972.html
Copyright © 2020-2023  润新知