关于字符串类型String的简单问题

mylzw 2008-06-02 08:48:10
先看一段代码:
var
Str:String;
begin
Form1.Caption:='Test';
Str:='1234567890';
GetWindowText(Handle,PChar(Str),10);
ShowMessage(Str);//错误:弹出1234567890
以上代码修改后:
var
Str:String;
begin
Form1.Caption:='Test';
Str:='1234567890';
@Str[1]; //此处只取地址,什么都不做
GetWindowText(Handle,PChar(Str),10);
ShowMessage(Str);//正确:弹出Test
end;
这两行代码只多了一个取地址的空操作,为何会产生这种结果?哪位高手指点下~
PS:大家现在都用的什么版本的Delphi啊? 大家用Delphi做数据库开发用都是哪个数据库访问组件?
...全文
216 16 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
mylzw 2008-06-04
  • 打赏
  • 举报
回复
多谢大家的参与,
僵哥的解释很科学~
skylkj 2008-06-03
  • 打赏
  • 举报
回复

showmessage(inttostr(integer(pchar(str))));
str:='12314554';
showmessage(inttostr(integer(pchar(str))));
@str[1];
showmessage(inttostr(integer(pchar(str))));


三次show出来的值是不一样的.
第一次第二次不一样很好解释,第三次的变化嘛...

http://topic.csdn.net/u/20080130/15/e1c5d3cf-1976-4edc-ae75-e0c536ff7777.html?1047010330
这里有解释
僵哥 2008-06-03
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 zdlou 的回复:]
代码段1:
var
Str:String;
begin
Form1.Caption:='Test';
Str:='1234567890';
GetWindowText(Handle,PChar(Str),10);
ShowMessage(Str);//错误:弹出1234567890

Str 结果为1234567890的原因是因为GetWindowText函数会把Form1的窗口标题Copy到内存缓冲区中,它需要一个首地址,但是上面的函数没有提供。同样,Str是一个字符串,它已经占用了用Str[1]..Str[10]这段内存地址,所以Show出来的仍然是Str…
[/Quote]
什么叫没有提供首地址?PChar(str)这个指向的就是str内容的首地址。
zdlou 2008-06-03
  • 打赏
  • 举报
回复
代码段1:
var
Str:String;
begin
Form1.Caption:='Test';
Str:='1234567890';
GetWindowText(Handle,PChar(Str),10);
ShowMessage(Str);//错误:弹出1234567890

Str 结果为1234567890的原因是因为GetWindowText函数会把Form1的窗口标题Copy到内存缓冲区中,它需要一个首地址,但是上面的函数没有提供。同样,Str是一个字符串,它已经占用了用Str[1]..Str[10]这段内存地址,所以Show出来的仍然是Str。

请注意以下代码的区别:
以上代码修改后:
var
Str:String;
begin
Form1.Caption:='Test';
Str:='1234567890';
@Str[1]; //此处只取地址,什么都不做
GetWindowText(Handle,PChar(Str),10);
ShowMessage(Str);//正确:弹出Test
end;
其中多了一个@Str[1]的取地址符,这也就是为指针(Pchar类型)提供了首地址的入口,调用GetWindowText函数时,会将Form1的窗口标题从这个入口处写起,最多写10个字符,其实Str字符串仍然是存在的,只是GetWindowText将窗口标题Copy到缓冲区时也会把一个结束符#0写到最后面,用以标志该串的结束,实际的字符串Str应该为:'Test'#0'67890'(可以用断点跟踪查下。)所以Show 出来的时候遇到#0就把字符串结束了,从而只剩下Test。
hsmserver 2008-06-03
  • 打赏
  • 举报
回复
谢谢,unsigned、blazingfire
明白了,呵呵
blazingfire 2008-06-03
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 hsmserver 的回复:]
如果是常量的话的引用同一内存地址
var
str1,str2:String;
str1:='1234567890';
str2:=str1;//这时是一样的
str2:=str1+'1';//这时重新分配内存

原来我一直理解为@str[1]指向原地址,应该是强制分配内存区域吧
[/Quote]
也不完全是这样的。象这样:
SetLength(S, 10);
@S[1];//==UniqueString是不会重新分配空间的,因为S的空间已经是独用的了!
hsmserver 2008-06-03
  • 打赏
  • 举报
回复
如果是常量的话的引用同一内存地址
var
str1,str2:String;
str1:='1234567890';
str2:=str1;//这时是一样的
str2:=str1+'1';//这时重新分配内存

原来我一直理解为@str[1]指向原地址,应该是强制分配内存区域吧
blazingfire 2008-06-03
  • 打赏
  • 举报
回复
Delphi的String是基于引用的方式来存储的,也就是说内一段用来存字符串的内存空间可以由一个以上的字符串所引用,见如下的例子:

procedure TForm1.Button1Click(Sender: TObject);

procedure WriteData(P: PChar);
var
i: Integer;
S: String;
begin
S := 'Test'#0;
for i := 1 to Length(S) do
begin
P^ := S[i];
Inc(P);
end;
end;

var
Str, S: string;
i: integer;
begin
Form1.Caption := 'Test';
SetLength(S, 10);
for i := 1 to Length(S) do
S[i] := Chr(Ord('0') + i - 1);
Str := S;//'0123456789'
WriteData(PChar(Str));//我们修改的是Str内容
ShowMessage(S);//S<>'0123456789'了,而为'Test'#0'56789',弹出的为'Test',#0以后的不显示
end;

所以说S := '0123456789'只是把S变量指向常量'0123456789'内容而已(也就是引用方式)
blazingfire 2008-06-03
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 unsigned 的回复:]
引用 6 楼 hsmserver 的回复:
PChar会重新分配内存
而@str[1]直接指向内存地址
不知这么说楼主能否明白

PChar不会重新分配内存,但是String[Index]会重新分配内存。

当直接写上str := '1234567890';的时候,str指向的是一个常量地址,也是一段不可改写的内存,所以GetWindowText的时候,实际上写内存失败,但是GetWindowText实际上交给窗体的消息处理函数执行的,就会报一个异常(这个异常不太准确),当你执行完Ge…
[/Quote]
Unsigned的解释是对的,hsmserver解释有问题。在没有调用@Str[1]时,Str这个字符串的引用内容是一个常量'1234567890',其内容是不可以修改的
可以作如下的实验:
procedure TForm1.Button1Click(Sender: TObject);

procedure WriteData(P: PChar);
var
i: Integer;
S: String;
begin
S := 'Test'#0;
for i := 1 to Length(S) do
begin
P^ := S[i];
Inc(P);
end;
end;

var
Str: string;
begin
Form1.Caption := 'Test';
Str := '1234567890';
//GetWindowText(Handle, PChar(Str), 10);
WriteData(PChar(Str));

ShowMessage(Str); //错误:弹出1234567890
end;

这段代码在执行的时候会出异常的,因为WriteData试图去修改不可修改(保护的)的内存。但是在WriteData(PChar(Str))之前加一句@Str[1]之类的话
就没有问题。那么@Str[1]到底作了什么呢?在调试一下Delphi原代码如下:

procedure _UniqueStringA(var str: AnsiString);
asm
JMP InternalUniqueString
end;

也就是说Delphi自动调用了UniqueString代码,也就是UniqueString(Str)。而UniqueString(Str)就是让Str所指向的内容只有Str一个人引用,因为
在Str := '1234567890'时,Str所引用的内容为为一常量'1234567890',所以UniqueString(Str)会把原内容Copy一份出来(分配内存,再复制内容),
这样再调WriteData(PChar(Str))就可以执行成功了。
hsmserver 2008-06-03
  • 打赏
  • 举报
回复
P := PChar(str);//取得str原始地址
P1 :=@str[1];//取得str新地址
//查看两个地址进行比较
ShowMessage(Format('str原地址=$%x,str新地址=$%x',[LongWord(P),LongWord(p1)]));
不好意思,我理解有误了,测试了一下如僵哥所言
向僵哥学习
僵哥 2008-06-03
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 hsmserver 的回复:]
PChar会重新分配内存
而@str[1]直接指向内存地址
不知这么说楼主能否明白
[/Quote]
PChar不会重新分配内存,但是String[Index]会重新分配内存。

当直接写上str := '1234567890';的时候,str指向的是一个常量地址,也是一段不可改写的内存,所以GetWindowText的时候,实际上写内存失败,但是GetWindowText实际上交给窗体的消息处理函数执行的,就会报一个异常(这个异常不太准确),当你执行完GetWindowText之后,可以通过 ShowMessage(format('错误号:%d,错误信息:%s',[GetLastError,SysErrorMessage(GetLastError)]));显示具体的错误,如果是正常完成的话错误号是0.

但是Str[1]这个是一个强制内存分配的动作。就会在原来Str的基础上复制一个串并交给Str,于是此时得到的是一个全新的地址,这个是一段堆内存,是可读写的。

最简单的测试:

var
str: String;
begin
Form1.Caption := '测试';
str := '1234567890';
GetWindowText(Form1.Handle,PChar(str)(*PChar只取str的实际内存存储空间首地址*),10);
ShowMessage(format('错误号:%d,错误信息:%s',[GetLastError,SysErrorMessage(GetLastError)]));\
ShowMessage(str);
end;



var
str: String;
P,P1: PChar;
begin
Form1.Caption := '测试';
str := '1234567890';
P := PChar(str);//取得str原始地址
@str[1];
P1 := PChar(str);//取得str新地址
//查看两个地址进行比较
ShowMessage(Format('str原地址=$%x,str新地址=$%x',[LongWord(P),LongWord(p1)]));

GetWindowText(Form1.Handle,PChar(str)(*PChar只取str的实际内存存储空间首地址*),10);
ShowMessage(format('错误号:%d,错误信息:%s',[GetLastError,SysErrorMessage(GetLastError)]));
ShowMessage(str);

ShowMessage(format('原串:%s,新串:%s',[strpas(p),strpas(p1)]));
end;
hsmserver 2008-06-03
  • 打赏
  • 举报
回复
PChar会重新分配内存
而@str[1]直接指向内存地址
不知这么说楼主能否明白
mylzw 2008-06-02
  • 打赏
  • 举报
回复
GetWindowText(Handle,@Str[1],10);
这样是能弹出正确的Test的,

@Str[1]; //此处只取地址,什么都不做
GetWindowText(Handle,PChar(Str),10);
也能弹出正确的Test,不知原理是什么。
喝口水 2008-06-02
  • 打赏
  • 举报
回复
procedure TForm1.Button1Click(Sender: TObject);
var
Str:String;
begin
Form1.Caption:='Test';
Str:='1234567890';
GetWindowText(Handle,PChar(@Str[1]),10);
ShowMessage(Str);//这儿弹出的是Test
end;
喝口水 2008-06-02
  • 打赏
  • 举报
回复
指针定位的问题吧,
我用D6,7
使用ADO
mylzw 2008-06-02
  • 打赏
  • 举报
回复
试试插入源代码功能~ :)

var
Str:String;
begin
Form1.Caption:='Test';
Str:='1234567890';
GetWindowText(Handle,PChar(Str),10);
ShowMessage(Str);//错误:弹出1234567890
以上代码修改后:
var
Str:String;
begin
Form1.Caption:='Test';
Str:='1234567890';
@Str[1]; //此处只取地址,什么都不做
GetWindowText(Handle,PChar(Str),10);
ShowMessage(Str);//正确:弹出Test
end;

16,747

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 语言基础/算法/系统设计
社区管理员
  • 语言基础/算法/系统设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧