var
p:pchar;procedure TForm1.Button1Click(Sender: TObject);
var
s:string;
begin
s:='aaa';
p:=PChar(s);
end;procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(p);//-->结果为'aaa';
end;以上,p为全局变量.s为局部变量,理论上在TForm1.Button1Click(Sender: TObject)作用域结束后,s占用的内存应该清空.为何在TForm1.Button2Click(Sender: TObject)中还能显示出'aaa'???
p:pchar;procedure TForm1.Button1Click(Sender: TObject);
var
s:string;
begin
s:='aaa';
p:=PChar(s);
end;procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(p);//-->结果为'aaa';
end;以上,p为全局变量.s为局部变量,理论上在TForm1.Button1Click(Sender: TObject)作用域结束后,s占用的内存应该清空.为何在TForm1.Button2Click(Sender: TObject)中还能显示出'aaa'???
解决方案 »
- Delphi7 创建的服务更改名称后无法启动
- 编写C/S结构的程序,如何连接internet网上的一台sqlserver数据库服务器
- 救命!!!电脑复制文本内容后粘贴不了。。
- 100分求Thashedstringlist的例子!
- 如何把 动态创建控件及其子倥件都流化到一个文件里,然后下次读出来动态创建?
- 大家帮我看一下这段代码,(读别人的程序)看不懂:
- 500 分求解(一次不够可以分几次给)
- Adoquery1的怪事,真不知是怎么搞地!
- delphi7怎么编写条形码?
- 在VC++里使用DELPHI写的COM组件时,导入其类型库后,报自己写组件的接口IAppServer未定义,哪位知道它定义在DELPHI的哪个文件里了?
- 全场的checkbox背景变颜色
- 有没有专门介绍indy组件页的整体说明呀
呵呵,这是不能抛开的。
“只是由于没有后续代码重新申请原来S分配的内存,并加以改写,所以'aaa'仍然存在.”
你可以验证一下,无论你怎么重新分配,都无法使p无效。因为,s仅仅是对这个常量指针的引用。
换句话说
procedure TForm1.Button1Click(Sender: TObject);
var
s:string;
begin
s:='aaa'; //这里s不会分配内存,并复制aaa
p:=PChar(s); //而这里,实际就是将p直接指向了常量内存
end;
或者你可以再作个测试:
procedure TForm1.Button1Click(Sender: TObject);
var
s:string;
begin
s:='aaa';
p:=PChar(s); ShowMessage(IntToStr(Integer(p)));
s[1] := 'b'; p := PChar(s);
ShowMessage(IntToStr(Integer(p)));
end;
你会发现2次ShowMessage的值是不 一样的。
第一个ShowMessage的地址显示是$004XXXXX 也就是在进程的地址空间(说明:没修改Image Base的情况下)
而第二个ShowMessage的地址显示则是一个与$004XXXXX差别很大的地址.
之所以产生这样结果的原因是 s[1] := 'b'导致了 s进行了值拷贝,也就是重新分配了内存,并将原内存的内容复制过来了.这些操作是由string的内部实现完成的.
ShowMessage(IntToHex(Integer(p), 8));
SetString(s,PChar('aaa'),3);//相当于一个复制操作
然后发现结果不是我所说的.那楼主不又要来找我麻烦了?
当然对于一般使用者,是感觉不到这种区别的.
var
p:PInteger; procedure TForm1.Button1Click(Sender: TObject);
var
s:Integer;
begin
s:= 125;
p:= @s;
end; procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(IntToStr(p^));//-->结果为125;
end; 那么 僵哥(不需要告诉别人你是否初学,求怜不如思进取)是完全正确的.但是涉及到string这种类型就有点区别了. 你暂时可以按 僵哥 的意思来理解.
const a='aaa';
var
s:string;
p1,p2,p3:PChar;
begin
s:='aaa';//s分配内存,从常量,赋值'aaa'
p1:=pchar(s);//p1肯定是一个野指针
p2:=pchar(a);//p2指向a的指针
p3:=pchar('aaa');//p3也是指向a的指针,因为aaa本身和常量a解释器认为是同一个指针的引用end;
var
s: string;
p: PChar;
begin
s := 'abc';
p := PChar(s); p^ := 'c';
ShowMessage(p);
end;这个是会崩溃的.
呵呵,你可以测试下: procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
p: PChar;
begin
s := 'abc';
p := PChar(s); p^ := 'c';
ShowMessage(p);
end; 这个是会崩溃的.
小伙子,你最好验证下哟.
procedure TForm1.Button1Click(Sender: TObject);
const a='aaa';
var
s:string;
p1,p2,p3:PChar;
begin
s:='aaa';//s分配内存,从常量,赋值'aaa'
p1:=pchar(s);//p1肯定是一个野指针
p2:=pchar(a);//p2指向a的指针
p3:=pchar('aaa');//p3也是指向a的指针,因为aaa本身和常量a解释器认为是同一个指针的引用 ShowMessage(IntToHex(Integer(p1), 8) + ' ' + IntToHex(Integer(p2), 8) + ' ' + IntToHex(Integer(p1), 8));
end;Delphi 7 测试输出:
00452A48 00452A4C 00452A48
让我知道了
s := 'aaa'和 p3 := pchar('aaa') 是一个地址.
不应该是
ShowMessage(IntToHex(Integer(p1), 8) + ' ' + IntToHex(Integer(p2), 8) + ' ' + IntToHex(Integer(p1), 8));
应该是
ShowMessage(IntToHex(Integer(p1), 8) + ' ' + IntToHex(Integer(p2), 8) + ' ' + IntToHex(Integer(p3), 8));
哦,的确错了,不好意思.
Delphi 7 测试输出:
00452A48 00452A4C 00452A4C
可以试一下把p2的赋值往前移,这样子就更加明显了
const a='aaa';
var
s:string;
p1,p2,p3:PChar;
begin
p2:=pchar(a);//p2指向a的指针
s:='aaa';//s分配内存,从常量,赋值'aaa'
p1:=pchar(s);//p1肯定是一个野指针
p3:=pchar('aaa');//p3也是指向a的指针,因为aaa本身和常量a解释器认为是同一个指针的引用 ShowMessage(IntToHex(Integer(P1),4));
ShowMessage(IntToHex(Integer(P2),4));
ShowMessage(IntToHex(Integer(P3),4));
end;
原因很简单,常量不能写. s := 'abc'仅是赋予s一个常量指针,并没有分配内存,并复制数据.
"只是由于没有后续代码重新申请原来S分配的内存,并加以改写,所以'aaa'仍然存在."
显然就楼主的代码来看是不对的.
紧接着的 p := PChar(s) 实际就是就该常量指针赋给了 p所以 p指针所指向内存并非不确定的.最后,我的一个猜测 常量地址可写,有可能是Delphi2007的一个BUG ^_^,常量可写还叫啥常量, 都用变量不就得了.
还好Delphi 2009又不可以写了.我也记得我用2007写测试程序时发现常量可写,当时百思想不得其解.
const a='aaa';
var
s:string;
p1,p2,p3:PChar;
begin
s:='aaa';//s是对常量a的一个引用,并未分配内存
p1:=pchar(s);//p1是一个指向s的指针,但是s是a的引用
p2:=pchar(a);//p2是一个指向常量a的指针
p3:=pchar('aaa');//同上
end;
等你经验丰富了之后,再来翻翻这个帖子吧.
s:='aaa';//s是对常量a的一个引用,并未分配内存
这个理解是对的,但是你要注意了:
const a='aaa';
var
s:string;
p1,p2,p3:PChar;
begin
s:='aaa';//s是对常量a的一个引用,并未分配内存
s[1] := 'a';
p1:=pchar(s);//p1是一个指向s的指针,但是s是a的引用
p2:=pchar(a);//p2是一个指向常量a的指针
p3:=pchar('aaa');//同上
end; 如果有了红色这句,s 会在这句上分配内存,复制原来'aaa'中的内容到新分配内存中.然后,再修改s[1].
所以,给你的感觉就像 s:='aaa'时已经分配过一样.
确切的来说,我对PE文件结构还不是十分了解,最近正在学着写PE文件结构分析程序.目前在搞导出表.
但是就我理解来说,代码执行是线性的,比如执行了一条MOV指令, EIP会自动增加,如果常量放在EIP的执行序列中(你说指的EIP附近),那么就需要频繁的JMP来跳过这些区域.
感觉正常的程序是不会这么做的,而通用的编译器就更不会自动做这些事情了.
const a='aaa';
var
s:string;
p1,p2,p3:PChar;
begin
s:='aaa';//s是对常量a的一个引用,并未分配内存
p1:=pchar(s);//p1是一个指向s的指针,但是s是a的引用 --->p1应该是指向'aaa'的指针吧
p2:=pchar(a);//p2是一个指向常量a的指针
p3:=pchar('aaa');//同上
end;
同意蒲石的观点。===========================================
s:='aaa'的指针跟const a='aaa'的指针不相同?但const a='aaa'跟pchar('aaa')相同?都相同。不过,如果s以后被重新赋值的话,则s的指向会改变。
说一下我的新观点:
1、对于下面的楼主的这段代码,'aaa'并非分配在常量数据段里。之所以ShowMessage时还能显示出'aaa',完全是因为这段被回收的空间暂时没有被再次占用。故我赞同僵哥的最初的观点。
var
p:pchar; procedure TForm1.Button1Click(Sender: TObject);
var
s:string;
begin
s:='aaa';
p:=PChar(s);
end; procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(p);//-->结果为'aaa';
end; 2、另一段程序的问题:
s:='aaa'的指针跟const a='aaa'的指针不相同?但const a='aaa'跟pchar('aaa')相同?
你的推断是正确的。s与a虽然都是'aaa'但两个分别在变量段和常量段,所以两个指针互不相同。而后边的pchar('aaa')中的'aaa'是常量,恰与a的值相同,故pchar('aaa')指向a所表示的'aaa'的内存地址。做了实验,即使改成s:=a;s与a的地址也不同。3、周爱民的《delphi源代码分析》的第15页有相关的内容,如有可能,你可以参考一下,相信会有收益。4、最后向僵哥和蒲石两位前辈表示敬意,同时再次向lz表示歉意,在你已经快明白的时候,我又把你弄糊涂了 :)
以上个人观点,欢迎指正。关注楼下能有高手继续让我和lz都有进步。
const a='aaa';
var
s:string;
p1,p2,p3:PChar;
begin
s:='aaa';//s是不是对常量a的一个引用,而是另外分配内存 ,两个一个是变量,一个是常量,分属不同区域,可以看看exe文件内容,可以佐证。
p1:=pchar(s);//p1是一个指向s的指针,故s不是a的引用
p2:=pchar(a);//p2是一个指向常量a的指针
p3:=pchar('aaa');//同上
end;
const a='aaa';
var
s:string;
p1,p2,p3:PChar;
begin
s:='aaa';//这里的'aaa'确实是一个常量,而是一个隐含的常量,但并不是常量a,s是对这个隐含常量的引用
p1:=pchar(s);//p1是一个指向s的指针
p2:=pchar(a);//p2是一个指向常量a的指针
p3:=pchar('aaa');//p3也是一个指向常量a的指针
end;
//事实证明,p2和p3都是指向常量a的指针,而p1是指向隐含常量'aaa'的指针。同时也证实了蒲石所说的p1是指向常量的指针的正确性,但是这个常量并不是常量a.