var S: string; P : PChar; begin S := FormatDatetime('YYYYMMMMDDhhnnsszzz', Now); P := PChar(S); Showmessage(Format('%p,%p,%p', [@S, @S[1], Pointer(P)])); {看一下区别。@S是取S变量的地址;而P := PChar(S)等效于P := @S[1]} {请注意!为什么S赋值没有用字符串常量?因为字符串常量的引用计数总是-1,P:=PChar(S)时会发生复制} end;
再给你一个演示,请仔细分析代码,看一下显示的五个地址var S: string; P : PChar; begin S := FormatDatetime('YYYYMMMMDDhhnnsszzz', Now); P := PChar(S); Showmessage(Format('%p,%p,%p,%p,%p', [Pointer(S), @S, @S[1], Pointer(P), Pointer(string((@S)^))])); end;
S: string;
P : PChar;
begin
S := FormatDatetime('YYYYMMMMDDhhnnsszzz', Now);
P := PChar(S);
Showmessage(Format('%p,%p,%p', [@S, @S[1], Pointer(P)]));
{看一下区别。@S是取S变量的地址;而P := PChar(S)等效于P := @S[1]}
{请注意!为什么S赋值没有用字符串常量?因为字符串常量的引用计数总是-1,P:=PChar(S)时会发生复制}
end;
S: string;
P : PChar;
begin
S := FormatDatetime('YYYYMMMMDDhhnnsszzz', Now);
P := PChar(S);
Showmessage(Format('%p,%p,%p,%p,%p', [Pointer(S), @S, @S[1], Pointer(P), Pointer(string((@S)^))]));
end;
昨天说的是'aaa',而不是全局变量t:string='aaa'
当然,没有什么本质的区别
编译前期就已经可以明确'aaa'的地址,是指第一个'a'所在的那个字节的地址(即@t[1]),而不是@t
@t是变量t所在的那四个字节的地址(这四个字节存放着@t[1])。从@t[1]处开始,是t的整个字符序列,直到#0
'aaa' 与 全局变量t:string='aaa' 的区别在于,前者没有变量名字。在这里,还需要说一下进程的内存格局:
按照C++书上的理论,一个程序的内存格局通常分为四个区:
1、全局数据区
2、代码区
3、栈
4、堆
不知Delphi编译器是否也是这样规划,但我想既然是Windows程序,应该也是这样的了。因此,对于全局变量S来说:
S(四个字节),在全局数据区。因此@S是指向全局数据区。其他全局变量如窗体、StringList,都在这里。(注意!只是变量在这里)
S[1],在堆内存中。当声明全局变量时,并不进行堆内存分配,只有为它赋值时,才进行分配。同理,窗体变量(如Form1)、StringList、BMP等等,其变量指向的空间(即实例所在空间),也在堆里。当Create时,它们的堆内存空间才被分配。
假如S被声明为局部变量,那么S在栈上,S所指向的字符序列,在堆上。
简单变量例如integer、Byte、Char等,全局的就在全局数据区、局部的就在栈上,变量名字就是它存放数据的地方。
根据上面说的一大堆,请理解下面的:注意,这里3楼的S是局部变量。Pointer(S) ---- 把位于栈上的S(四个字节)转换为指针类型使用,即:取S变量所在四个字节空间的内容@S ---- S位于栈上,占四个字节,但这四个字节在具体的什么地址?@S即是。说到这里停一下,请注意,一个是四个字节的内容,一个是四个字节的位置@S[1] ---- 这个,在前面楼层已经讲了,这是S所指向的堆内存字符序列的第一个字符的地址Pointer(P) ---- 这个,就是把P的内容读出来,与Pointer(S)一样Pointer(string((@S)^)) ---- 这个看起来复杂,实际上是写法上的重复,只是为了加深理解@S:
@S前面已讲,是S所在栈内存的位置。因此,(@S)^就是S。于是,String((@S)^)还是S。
于是Pointer(string((@S)^))就是Pointer(S)