1. 程式人生 > >pchar,pwidechar,pansichar作為返回參數時內存訪問錯誤

pchar,pwidechar,pansichar作為返回參數時內存訪問錯誤

系統 ron ptr gef move begin .com archive ast

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;

改成全局變量保存的話,可以正常訪問到數據




以下是萬一的博客中關於字符串分配內存
萬一的博客 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:\WINDOWS\system32}
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)分配內存, 當然就不僅僅用於字符指針了. 還是到後面專題再做例子吧.

pchar,pwidechar,pansichar作為返回參數時內存訪問錯誤