PChar String 使用问题!!!! ( 积分: 50 )

  • 主题发起人 主题发起人 bjyplbx
  • 开始时间 开始时间
B

bjyplbx

Unregistered / Unconfirmed
GUEST, unregistred user!
function getstr: string;
begin
result := 'Macroview';
end;

function getpachar1(a: PChar): PChar;
var
s1 : string;
begin
s1 := StrPas(a);
result := PChar(s1);
end;

function getpachar2(a: PChar): PChar;
var
s1, s2 : string;
i : integer;
begin
s1 := StrPas(a);
s2 := '';
for i := 1 to length(s1) do
begin
s2 := s2 + s1;
end;
result := PChar(s2);
end;

procedure TForm1.Button3Click(Sender: TObject);
var
s1 : string;
i : integer;
a, b, c : PChar;
begin
s1 := getstr;
a := PChar(s1);
b := getpachar1(a);
c := getpachar2(a);
showmessage(StrPas(a));
showmessage(StrPas(b));
showmessage('/' + StrPas(c) + '/');
end;

procedure TForm1.Button4Click(Sender: TObject);
var
s1 : string;
a, b : PChar;
begin
s1 := getstr;
a := PChar(s1);
b := getpachar2(a);
showmessage(StrPas(a));
showmessage(StrPas(b));
end;


显示结果非常不理想,完全都不一样啊,是地址的问题吗?
 
function getstr: string;
begin
result := 'Macroview';
end;

function getpachar1(a: PChar): PChar;
var
s1 : string;
begin
s1 := StrPas(a);
result := PChar(s1);
end;

function getpachar2(a: PChar): PChar;
var
s1, s2 : string;
i : integer;
begin
s1 := StrPas(a);
s2 := '';
for i := 1 to length(s1) do
begin
s2 := s2 + s1;
end;
result := PChar(s2);
end;

procedure TForm1.Button3Click(Sender: TObject);
var
s1 : string;
i : integer;
a, b, c : PChar;
begin
s1 := getstr;
a := PChar(s1);
b := getpachar1(a);
c := getpachar2(a);
showmessage(StrPas(a));
showmessage(StrPas(b));
showmessage('/' + StrPas(c) + '/');
end;

procedure TForm1.Button4Click(Sender: TObject);
var
s1 : string;
a, b : PChar;
begin
s1 := getstr;
a := PChar(s1);
b := getpachar2(a);
showmessage(StrPas(a));
showmessage(StrPas(b));
end;


显示结果非常不理想,完全都不一样啊,是地址的问题吗?
 
用法有问题
string是局部变量,返回后就被释放掉了
所以后两个函数因返回PChar得到的地址早就不可靠了

而返回string本身则没问题,因为其又被复制了一份
 
还有那个strpas用不着了,直接赋值就行,string类型自动转换
 
StrPas 为局部变量 s1 分配空间并将 Pchar 参数的实际内容复制到 s1,函数返回时 s1 的空间没有释放,所有内容是正确的。s2 是局部变量,函数返回时 s2 自动释放,所以其内容不确定。
getpachar2 函数增加一行,结果就相同了。
function getpachar2(a: PChar): PChar;
var
s1, s2 : string;
i : integer;
begin
s1 := StrPas(a);
s2 := '';
for i := 1 to length(s1) do
begin
s2 := s2 + s1;
end;
[red]s2:=StrPas(PChar(s2));[/red]
result := PChar(s2);
end;
注意,函数返回值为 PChar 类型,一定要为其人工分配空间。
 
function getpachar1(a: PChar): PChar;
var
s1 : string;
b : PChar;
begin
s1 := StrPas(a);
strlcopy(b, PChar(s1), length(s1));
result := b;
end;

function getpachar2(a: PChar): PChar;
var
s1, s2 : string;
i : integer;
b : PChar;
begin
s1 := StrPas(a);
s2 := '';
for i := 1 to length(s1) do
begin
s2 := s2 + s1;
Form1.Memo1.Lines.Add(s2 + ' | ' + s1);
end;
strlcopy(b, PChar(s2), length(s2));
result := b;
end;

这样倒是结果对了,但是可靠吗?看着很奇怪,请仔细讲解一下这里的问题。
 
就是我前面讲的局部变量空间被释放的原因。通过 strlcopy 或 StrPas 后,分配了固定的空间,所以结果是对的。
 
to kaida:
按照你说的改了,似乎还是不成啊
 
现在的strpas是为兼容性而保留的,没有实际的作用了

如果想用Pchar传递的话,应该像用API一样,调用函数负责分配字符串空间,传递该指针到被调用函数
被调用函数只把string拷贝到该空间,不负责内存管理

一般地,为了防止内存溢出,还应该传递一个缓冲区长度,那么被调用函数做内存拷贝时须注意不能超出这个最大长度
 
to someset:
很有道理,我试试。
 
后改的那两个函数,别开玩笑了!

指针没分配空间甚至还没定义你就用了,不崩溃只能说是运气!

string和pchar不一样,它是一个对象,有生存周期的管理能力

当Pchar赋值给string时,(不管用不用strpas)string将自动分配空间并拷贝pchar所指向的内容
而string赋给pchar时,仅仅将第一个字符处的内存空间赋给pchar
这样string释放后pchar就成了“悬挂”指针

返回string对象相当于string之间的赋值,过程是原string记录引用次数为2,并把指针传给新对象;原string对象跟着释放,但由于知道另有引用存在,所以没有最后释放内存,只是将引用计数减1;哪个对象将引用计数减到0,它就负责释放内存
这个过程是由于叫做Copy-On-Write的技术
 
kaida仁兄的这句:)
s2:=StrPas(PChar(s2));
呵呵,结果还是会被释放
s2原已经有空间,这句里会产生一个临时对象,s2会将自己析构并拷贝此临时对象,此过程完成后,临时对象也被删除,一切没什么变化,就象没发生过,函数返回时照样被析构掉

话说回来,结果是不是正确的这要看环境
“悬挂指针”不代表其没有内容,只不过可能内容没有发生过变化,但空间已经被收回,难免被其他代码再次利用,那么该地址也就被改写了:)
 
to someset:

function DelBadChar(Sbuf: PChar
Dbuf: PChar
Buf_Size: DWORD): boolean;
var
i : integer;
TmpStr1, TmpStr2 : string;
begin
TmpStr1 := StrPas(Sbuf);
TmpStr2 := '';
i := 1;
while i < (length(TmpStr1) + 1) do
begin
case TestByteType(TmpStr1, i) of
isErrorByte:
begin
inc(i);
end;
isSingleByte:
begin
TmpStr2 := TmpStr2 + TmpStr1;
inc(i);
end;
isLeadByte:
begin
TmpStr2 := TmpStr2 + TmpStr1 + TmpStr1[i + 1];
inc(i, 2);
end;
end;
end;
if length(TmpStr2) < Buf_Size then
begin
StrLCopy(Dbuf, PChar(TmpStr2), Buf_Size);
result := true;
end
else
result := false;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
s1, s2 : string;
Sbuf, Dbuf : PChar;
Buf_Size : DWORD;
begin
s1 := 'aaaaaaaaaaaa';
s2 := '';
Buf_Size := length(s1) + 1;
Sbuf := PChar(s1);
getmem(Dbuf, Buf_Size);
try
if DelBadChar(Sbuf, Dbuf, Buf_Size) then
s2 := StrPas(Dbuf)
else
s2 := '';
finally
freemem(Dbuf);
end;
ShowMessage(s2)

end;

是不是这样就可以了?有没有多余的地方,请指正。
 
粗看起来没什么问题了
其实不用API或其他C语言类的DLL的话,没必要费周章用Pchar,string如此智能又能降低出错,为什么不用呢?

多余的嘛,就是我说的strpas函数不用了,你真的不信啊?[:D]
要不是你用的低版本delphi?[:)]
 
对了,DelBadChar(Sbuf, Dbuf, Buf_Size-1)
BufSize要减1使用,以便留出串结束符'/0'的空间
 
to someset:
我就是把 DelBadChar 写个进一个 DLL 给别 PB 用,所以想用 PChar。
关于 strpas 在帮助里也是你那么说的,我忘改了。

关于 Sbuf 这个变量在 Button1Click 退出之后,我的程序是不是就不再占用他了,不会吃掉内存吧。比如我是从数据库取记录

table1.first;
while not table1.eof do
begin
s1 := vartostrdef(table1.fieldbyname['name'],'');
s2 := '';
Buf_Size := length(s1) + 1;
Sbuf := PChar(s1);
getmem(Dbuf, Buf_Size);
try
if DelBadChar(Sbuf, Dbuf, Buf_Size) then
s2 := Dbuf
else
s2 := '';
finally
freemem(Dbuf);
end;
//.....s2.....
table1.next;
end;

这样循环不会有问题吧?还有,这么频繁的 getmem 是不是不好?
 
内存分配会在一定程度上影响效率
要不你可以一次分配一个足够大的空间,以字段最大长度为参考的
这样可以不用频繁的分配了

sBuf指向的是s1的空间,而s1的空间会在结束时自己释放,所以就是说Sbuf的生存期等于s1的生存期
其实若不是非法使用内存的话,只须关注自己手动分配过内存的就行了,其他的管理职责不在你
 
哦。

给分了
 
我是在做删除半个汉字(乱字符)的函数,本来想用 bytetype 的,结果不好使,只能自己做个,也不知道好不好使呢,呵呵,谢谢大家。
 
虽然已经结帖,为了完整,我还是想补充一下:
function getpachar(a: PChar): PChar;
var
p: PChar;
begin
GetMem(p, Length(a));//注意分配内存
StrCopy(p, a);
result := p;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
p:PChar;
begin
p:= getpachar('Macroview');
...
FreeMem(p);//注意释放内存
end;
我上面说的StrPas确实不分配内存,应修正。
 

Similar threads

A
回复
0
查看
981
Andreas Hausladen
A
S
回复
0
查看
688
SUNSTONE的Delphi笔记
S
S
回复
0
查看
683
SUNSTONE的Delphi笔记
S
A
回复
0
查看
964
Andreas Hausladen
A
I
回复
0
查看
503
import
I
后退
顶部