有关PChar的问题 ( 积分: 100 )

  • 主题发起人 主题发起人 luckymanman
  • 开始时间 开始时间
L

luckymanman

Unregistered / Unconfirmed
GUEST, unregistred user!
//从文件流读出以0为结束符的字符串
procedure ReadNULLStr(const Offset: integer
var Result: pchar);
var
c: char;
P: pchar;
begin
P := Result;
FileStream.Position := Offset;
FileStream.Read(c, 1);
while c <> #0 do begin
P^ := c;//**************有时会出错
Inc(P);
FileStream.Read(c, 1);
end;
P^ := #0;//***************有时会出错
end;


调用如下:

var str: PChar;
begin
str := StrAlloc(512);
...
ReadNULLStr(Offset,str);//这里不停使用str变量
...
StrDispose(str);
end;

请问该函数有错吗?错在哪?为何有时会正常,有时会出错。是调用处出错还是函数本身有错呢?请指教
 
//从文件流读出以0为结束符的字符串
procedure ReadNULLStr(const Offset: integer
var Result: pchar);
var
c: char;
P: pchar;
begin
P := Result;
FileStream.Position := Offset;
FileStream.Read(c, 1);
while c <> #0 do begin
P^ := c;//**************有时会出错
Inc(P);
FileStream.Read(c, 1);
end;
P^ := #0;//***************有时会出错
end;


调用如下:

var str: PChar;
begin
str := StrAlloc(512);
...
ReadNULLStr(Offset,str);//这里不停使用str变量
...
StrDispose(str);
end;

请问该函数有错吗?错在哪?为何有时会正常,有时会出错。是调用处出错还是函数本身有错呢?请指教
 
P := Result

用法错误。 PChar的用法不同于string

string和Char数组都是一块内存, 其中存放连续的字符. string保存具体字符的内存对用户是透明的, 由Delphi管理它的分配, 复制和释放;
PChar是一个指针, 它的大小只有32位. 定义时由Delphi自动填0. 要将PChar作为字符串
使用的话必须自己分配内存用完必须自己释放。
对于对pchar的处理delphi有专用的函数, 详细请参照SysUtils单元以及帮助
 
to dwf3110:
P:=Result;//这是指针赋值,将P指向Result指向的内存,为什么不对呢?如果不对,那应该怎么改?
我已分配了内存,见调用处.

当然,上面的ReadNULLStr函数也可以这样写:
function ReadNULLStr(const Offset: integer): string;
var c: byte;
begin
Result := '';
FileStream.Position := Offset;
FileStream.Read(c, 1);
while c <> 0 do begin
Result := Result + Chr(c);
FileStream.Read(c, 1);
end;
end;
但这样效率大大降低,所以才不采用,读20多万字符串比用pchar慢几秒,而使用pchar不到1秒.
 
while c <> #0 do begin
P^ := c;//**************有时会出错//超过了512你分配的空间 str := StrAlloc(512);
Inc(P);
FileStream.Read(c, 1);
end;
P^ := #0;//***************有时会出错
 
to luckymanman 其实你的过程可以优化成这样啊:

function ReadNULLStr(const Offset: integer): string;
var
c: byte;
nSize: Integer;
begin
Result := '';
nSize := 0;
FileStream.Position := Offset;
FileStream.Read(c, 1);
while c <> 0 do
begin
Inc(nSize);
FileStream.Read(c, 1);
end;
SetLength(Result, nSize);
FileStream.Position := Offset;
FileStream.Read(Pchar(Result)^, nSize);
end;

当然,更好的方法应该是提高缓冲区的大小,如一次读入 N K 的数据,在内存中检索定位。
 
错误的原因是内存分配的错误,如果你分配了512字节的内存,当读到513的时候就报错了!
 
to hfghfghfg:
内存是足够的,即使分配51200的内存也会出错.

to LSUPER:
你这样的函数也比不上pchar快,因为的全部参数采用pchar传递会快很多.

上面的函数不知道哪里有问题,知道的朋友请指正一下吧.
 
to luckymanman, 使用 string 不一定比 pchar 来得慢,关键是核心方法的实现。
string 比 pchar 慢的根源是 delphi 会自动的为 string 分配内存,如: result := reslut + 'xxx' 这种代码,导致 delphi 重新分配、复制内存。
使用 pchar 由于是自己分配的内存空间,保证只在需要是才分配,避免象 string 的“自动”“频繁”分配,所以一般的比 string 来得高效。
换句话说,只要有限避免 string 的“自动”“频繁”分配,string 不见得比使用 pchar 效率低。
上面对 ReadNULLStr 就是这个原则。
or 等你改好 pchar 版本后可以对比一下,只要算法一样,效率绝对差不到哪去 ...
 
内存是足够的
那只能是
FileStream.Read(c, 1);
到最后 还没有 #0
或者
str := StrAlloc(512)
〉getmem


Description

StrAlloc allocates a buffer for a null-terminated string with a maximum length of Size - 1 (1 byte must be reserved for the termination character). The result points to the location where the first character of the string is to be stored. A 32-bit number giving the total amount of memory allocated is stored in the four bytes preceding the first character
it is equal to Size + 4. If space for a string is allocated with StrAlloc, it should be deallocated via StrDispose.

Because long strings are implicitly null terminated and dynamically allocated, the use of StrAlloc is in decline.
 
找到问题了,原来是有一个局部变量没有初始化,而这个程序刚好只读3个字节,所以有时会造成最高字节不是0而导致出错.
FOffset:=0;//加上这句
FileStream.Read(FOffset, 3);

to LSUPER:
原来我上面的函数是正确的,现在执行没有任何问题了.
你说的没错,string的确如此.但我还是选择pchar,因为用在这里比较高效,纯指针操作,只读一次流,而且不需要转换.

多谢大家的回复.
 
对于楼主的追求精益求精的做法,感到佩服。

我找了以前的一篇文章可以看看:

string和Char数组都是一块内存, 其中存放连续的字符. string保存具体字符的内存对用户 是透明的, 由Delphi管理它的分配, 复制和释放, 用户不能干预(其实也可以, 不过是通过非法途径). Char数组就不必说了吧?
PChar是一个指针, 它的大小只有32位. 定义时由Delphi自动填0. 要将PChar作为字符串使用的话必须自己分配内存用完必须自己释放. PChar型字符串由#0表示字符串结尾。
Delphi所提供的相关PChar字符串的操作都是判断#0来决定字符串的结尾的。
因为PChar是指针,所以它能指向任何地方(也就是说它不一定非要指向字符串不可).
把一个String赋值给PChar只是将String中保存具体字符串的内存的地址给PChar变量. 当然也可以把Char数组第一个元素的地址给PChar.
至于 哪个占用内存小, Char数组<PChar(指分配过字符串的)<string(除了具体字符串外
还 包含字符串长度)。
如果空字符串那么PChar<String<array [0..n] of Char
从速度来说毫无疑问string最慢, 例如:
作为参数传递(非var调用时)给过程时string将整个字串的副本传递过去, PChar将指针本身的副本传递过去(32位), Char数组和PChar一样, 传递的是第一个元素的地址副本.
不过就灵活性来说string最高, 而且Delphi支持的函数最多. 另外可以将String作为
 
符串的指针pchar的区别与联系
1、使用指向字符串的指针,如果不是以0结尾,运行时就会出现错误。为了避免这种错误,需要在字符串结尾人工加入0 即char(0),或用strpcopy函数在字符串结尾自动加0。
如:
p:pchar
p:=@s[1];{不是以0结尾,莫用pchar型指针}

用strpcopy函数赋值会在字符串s结尾自动加0。
strpcopy(p,'new');{strpcopy函数在字符串结尾自动加0}
 
上面的这些东西希望能给楼主一些找问题的提示。
 
无泪,上面已经说过了,问题解决了
多谢大家的参与.
 
后退
顶部