delphi 指针

 1 type
 2 Pint = ^Integer; //Pint 为类型 = 3 var
 4 i : Integer;
 5 pi : PInteger;
 6 Pintd : ^Integer; //Pintd为变量 : 号
 7 begin
 8 i := 100;
 9 pi := @i;
10 pi^ := 102;
11 ShowMessage(IntToStr(pi^));
12 Pintd := @i;
13 Pintd^ := 104;
14 ShowMessage(IntToStr(pi^));
15 end;

内存中的数据除了 0 便是 1, 你把它当作图片、字符、数字等等, 那是你的事, 内存只认识 0 和 1.

Win32 系统除了使用硬内存以外, 还可以从硬盘上开辟虚拟内存;

因为 Win32 的内存地址范围在 4 个 G 以内(0..232-1), 所以它最多能够给一个应用程序分配 4G 的运行空间; 并且其中的 2G 有系统管理, 实际上程序只有 2G 的自主空间. 还记得有说 String 最大长度是 2G 吗? 就是这个道理.

有 4G 的内存, 就有 4G 个地址, 也就是最多可以有 (1024*1024*1024*4 - 1 = 4294967295) 个内存地址, 这刚好是 Delphi 中 Cardinal 的最大值, 所以 32 位的指针类型追到底都是 Cardinal 类型的一个数字.

一个内存地址是 0..4294967295 之间的一个数字, 你可以通过内存地址读取或写入数据;
一个指针要用来索引或标识内存, 它也是 0..4294967295 之间的一个数字; 它们虽不相同, 但通过指针可以找到实际存储数据的内存地址, 并按指定的类型去读写它.

譬如:


var
  str: string;
  n: Cardinal;
  pstr: PString;
begin
  str := 'ABCDE';
  n := Cardinal(str); {获取内存地址}
  pstr := @str;       {现在 pstr 是 str 的指针}

  {n 与 pstr 的数字结果是(结果是随机的, 知道不一样就行了):}
  ShowMessage(IntToStr(n));              {4571092}
  ShowMessage(IntToStr(Cardinal(pstr))); {1244652}

  {但通过 pstr 可以找到 str}
  ShowMessage(pstr^); {ABCDE}
end;

程序运行后, 字符串所在的内存基本上是下面这个样子(以字节为单位), 上例中的 n 标识着 ↓ 的位置:

 
A B C D E


换二进制图示一下:

 
00001010 00001011 00001100 00001101 00001110


如果只看二进制, 这个数据到底是什么很难知道; 再说它为什么非得是字符串 "ABCDE" 呢? 这可不一定.

下面的例子中, 我们先是权且把它当作字符串, 但随着指针的移动, 字符串也在变化.

然后, 有分别把它分别用 Byte 指针(PByte) 和 Integer 指针(PInteger) 去读取它, 也会得到相应的值.

完整示例如下:


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  str: string;
  ps: PChar;
  n: Cardinal;
begin
  str := 'ABCDE';
  ps := PChar(str);
  n := Cardinal(ps);
  //n := Cardinal(str); {这行可以代替上面两行}
  ShowMessage(IntToStr(n)); {结果是 Windows 随机管理的}

  ShowMessage(PChar(n));   {ABCDE}
  ShowMessage(PChar(n+1)); {BCDE}
  ShowMessage(PChar(n+2)); {CDE}
  ShowMessage(PChar(n+3)); {DE}
  ShowMessage(PChar(n+4)); {E}
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  str: string;
  n: Cardinal;
  pb: PByte;
begin
  str := 'ABCDE';
  n := Cardinal(str);
  ShowMessage(IntToStr(n)); {4571140; 这是我这里的结果, 这是随机的}

  pb := PByte(n);
  ShowMessage(IntToStr(pb^)); {65}
  pb := PByte(n+1);
  ShowMessage(IntToStr(pb^)); {66}
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  str: string;
  n: Cardinal;
  pint: PInteger;
begin
  str := 'ABCDE';
  n := Cardinal(str);
  ShowMessage(IntToStr(n)); {4571140; 这是我这里的结果, 这是随机的}

  pint := PInteger(n);
  ShowMessage(IntToStr(pint^)); {1145258561}
  pint := PInteger(n+1);
  ShowMessage(IntToStr(pint^)); {1162101570}
end;

end.

上面的第三个程序段的结果或许让你迷惑: 
第一个结果, 应该和 "ABCD" 有点关系才对啊, 怎么是: 1145258561 ?
第二个结果, 应该和 "BCDE" 有点关系才对啊, 怎么是: 1162101570 ?

为什么呢? 这当然没错, 听我解释:
1145258561 转换成十六进制是: 44434241, 写得清楚一点是: $44 $43 $42 $41; 还记得 Intel 等当下流行的 CPU 安排数据是倒着的吗?

自己算算下一个, 用附件中的计算器即可.

----------------------------------------------------------------------------------------------------------------------------

FeedBack:

 #1楼  2008-03-07 17:13 maxcool[未注册用户]
请教2个小小问题: 
引用:1145258561 转换成十六进制是: 44434241, 写得清楚一点是: $44 $43 $42 $41; 还记得 Inter 等当下流行的 CPU 安排数据是倒着的吗? 
+++ 
1)是Intel不应是Inter吧。 
2)CPU数据倒着排,可是CPU不是内存,与内存有关系吗。
  回复引用
#2楼  2008-03-07 17:21 maxcool[未注册用户]
我对指针的理解,万一老师看对不对 

其实指针记录的是某个变量的数据的内存地址。 

就好像是个索引。 

比如图书馆有本书,拿到书的指针等于其实等于拿到那本书的具体位置,例如某排某列。而要拿到那本书,要追踪到这个指针的具体地址。换句话说,拿到指针,只拿到一张纸条(上面记录书的具体位置);要拿到书这个物品,要根据这个纸条上的地址去取。指针怎么跟藏宝地图似的呵呵。 

指针的指针相当于什么,相当于这个记录书位置纸条的位置。。。。不知道会不会变态到指针的指针的指针,估计没有需要吧? 

警察破案,找到张三,张三供出李四,那么张三就是李四的指针,李四供出王五,那么李四就是王五的指针。张三就是五五指针的指针。。。。。 

以上理解对不,请万一老师与各位访客批评指正!
  回复引用
#3楼  2008-03-07 17:23 maxcool[未注册用户]
那么我想问,为什么程序里要设置指针呢?这个问题好像很愚蠢,但是好多初学者很迷惑。。。。
  回复引用
#4楼[楼主]  2008-03-07 17:31 万一  
@maxcool: 
你说的问题我改了; 
关于 CPU 与内存: 内存是由 CPU 管理的, 它爱怎么用就怎么用; 
你说指针就像个索引, 我觉得完全对. 但指针本身只是个编号, 它还要指向一个地址; 
为什么要用指针呢? 回答: 假如你说的图书馆的书没有编号...
  回复引用
#5楼  2008-03-07 17:32 maxcool[未注册用户]
这里有一篇虽然比不上万一老师的,但是也不错,与大家共享 
http://hi.baidu.com/ton666/blog/item/545111016307700d7bec2c90.html
  回复引用
#6楼  2008-03-07 21:35 wuzhong[未注册用户]
@maxcool: 
你说的很有理,其实我个人认为可以这样理解指针. 
@del: 
感谢你的热诚,你分析问题的思路很清晰,也能见你的基础知识很牢固. 

那我还有一个问题: 
我们是不是可以把每一个变量(或者实例)都看成两个部分, 
TNode=Record 
Data:SomeType; 
Next:Pointer; 
end; 

这些变量就是通过链表的方式存储在内存里(我没学过数据结构,不知道可不可以这样理解),通过压栈的方式存入到内存中(后面一句是蒙的)。 
  回复引用
#7楼[楼主]  2008-03-07 21:56 万一  
@wuzhong: 我明白你的意思了. 
一个变量和结构关系不大(当然也可以把结构定义成一个变量), 和链表也没有关系; 反过来关系倒是很大. 
这样吧, 我再试着探讨一下结构和类的指针情况. 
周一吧, 周六周日很忙.
  回复引用
#8楼  2008-05-19 19:27 厨师[未注册用户]
请问万老师。数据倒着排列到底是啥原理。。。我用C32打开一个程序时候。比如有时候看到些字符。比如task....没发现数据是倒着排的呀。都是从左到右
  回复引用
#9楼[楼主]  2008-05-19 23:05 万一  
@厨师: 
譬如一个整数: 四字节的整数 2864434397, 它的四个字节分别是: 
$AA $BB $CC $DD; 但这只是我们的思维方式, 在 Intel CPU 会这样管理它们: $DD $CC $BB $AA 
至于不管用什么工具观察到的, 如果不是这样, 那是工具原因.
  回复引用
#10楼  2008-06-06 17:46 厨师[未注册用户]
还记得 Intel 等当下流行的 CPU 安排数据是倒着的吗? 


//万老师能详细教教这句话如何理解么。。。数据倒着排列是啥意思啊。OD打开的程序都是倒着排列啊?
  回复引用
#11楼  2008-06-06 18:04 厨师[未注册用户]
老师还有几个疑问啊 

1,。。ShowMessage(PChar(n)); {ABCDE} 
为什么不是pchar(n)^ 后几个都是用了^啊 
2, 
为什么最后一个是倒着排列 为什么最后一个才提到CPU倒着排列数据,而前几个也没牵扯到这个问题 

  回复引用
#12楼[楼主]  2008-06-06 18:15 万一  
@厨师: 
譬如一个四字节的整数 1145258561, 它的十六进制表示是: $44434241, 每个字节分别是: $44 $43 $42 $41, 但 Inter CPU 是这样安排它们的: $41 $42 $43 $44, 这就是我说的 "倒" 着. 

关于 "为什么不是pchar(n)^ 后几个都是用了^啊?" 
因为后面几个都是定义的指针类型, 譬如: pb: PByte; pint: PInteger; 
  回复引用
#13楼  2008-06-06 19:42 厨师[未注册用户]
但指针本身只是个编号, 它还要指向一个地址; 

//这句我是这样理解的。指针编号本身就是个地址。指针其实可以理解为就是一个指针变量。这个变量存储的数据是另一个变量的地址! 不知道这样理解对不老师。指针是确实存在的一个东西。不是一个抽象的东西! 对不
  回复引用
#14楼[楼主]  2008-06-06 20:08 万一  
我觉得你理解的对. 
我的理解是: 系统用链表来管理指针, 这个链表中的元素起码有 -- 指针、指针类型、执向的地址.
  回复引用
#15楼  2008-09-02 08:59 duoluo[未注册用户]
老师我有问题请教 

我要读写某程序的数据,已经知道了在数据段的内存地址 
我想问读写数据的时候指针的类型是不是必须和数据的类型一致
  回复引用
#16楼[楼主]  2008-09-02 10:06 万一  
@duoluo 
这不一定, 要根据需要.
  回复引用
#17楼  2008-09-05 08:39 Delphier[未注册用户]
--引用-------------------------------------------------- 
万一: @厨师: 
<br>譬如一个四字节的整数 1145258561, 它的十六进制表示是: $44434241, 每个字节分别是: $44 $43 $42 $41, 但 Inter CPU 是这样安排它们的: $41 $42 $43 $44, 这就是我说的 &quot;倒&quot; 着. 
<br> 
<br>关于 &quot;为什么不是pchar(n)^ 后几个都是用了^啊?&quot; 
<br>因为后面几个都是定义的指针类型, 譬如: pb: PByte; pint: PInteger; 
<br> 
-------------------------------------------------------- 

譬如一个四字节的整数 1145258561,这不是五个字节吗?晕了
  回复引用
#18楼[楼主]  2008-09-05 08:47 万一  
@Delphier 
这里的 1145258561 是十进制.
  回复引用
#19楼  2009-04-20 22:38 下饭菜  
我一直有个疑问:delphi中的string类型变量是不是指针,如果是指针,如何获得指针中保存的地址?
  回复引用
#20楼  2009-04-20 22:45 下饭菜  
如果不是指针,那么第一例中: 
n := Cardinal(str); {获取内存地址} 
pstr := @str; {现在 pstr 是 str 的指针} 

{n 与 pstr 的数字结果是(结果是随机的, 知道不一样就行了):} 
ShowMessage(IntToStr(n)); {4571092} 
ShowMessage(IntToStr(Cardinal(pstr))); {1244652} 

两个内存地址就应该一样
  回复引用
#21楼  2009-04-20 23:02 下饭菜  
能够解释一下第一例中两个地址的含义么?到底是4571092代表了“ABCDE”的真实地址,还是1244652?另一个又代表什么?
  回复引用
#22楼[楼主]  2009-04-21 09:41 万一  
@下饭菜 
一个是字符串的起点位置, 一个是指向这个位置的指针.
  回复引用
#23楼  2009-05-19 23:57 dragon10001[未注册用户]
万老师,这里可不可以这样理解,4571092代表了“ABCDE”的真实地址,1244652代表了指向这个位置的指针的地址,ShowMessage(IntToStr(Cardinal(pstr)))中的(Cardinal(pstr)可以看做是取指针的地址,和n := Cardinal(str)是相同的,是不是这样?
  回复引用
#24楼  2009-06-10 18:44 小亮子  
万一老师: 
  你好! 
  在您的博客上学到的东西比看书真是实在! 
  上面的例子我在d2009中测试了下,发现如果如以下语句,d2009会出现乱码: 
ShowMessage(PChar(n)); {ABCDE} 
ShowMessage(PChar(n+1)); {BCDE} 
ShowMessage(PChar(n+2)); {CDE} 
ShowMessage(PChar(n+3)); {DE} 
ShowMessage(PChar(n+4)); {E} 
改为: 
ShowMessage(PChar(n)); {ABCDE} 
ShowMessage(PChar(n+2)); {BCDE} 
ShowMessage(PChar(n+4)); {CDE} 
ShowMessage(PChar(n+6)); {DE} 
ShowMessage(PChar(n+8)); {E} 
运行正常! 
  想来是d2009的string类型默认的是unicode字符吧!
  回复引用
#25楼  2009-06-16 09:25 wt_yxs[未注册用户]
procedure TForm1.Button1Click(Sender: TObject); 
var 
a:string; 
n: Cardinal; 
begin 
a:='万一'; 
n:=Cardinal(a); //结果是随机的, 知道不一样就行了 我想要的是变量a的内存地址,这里OK,请往下 
edit1.Text:=inttostr(n); 
end; 

procedure TForm1.Button2Click(Sender: TObject); 
var 
a:char; 
n: Cardinal; 
begin 
a:='A'; 
n:=Cardinal(a); 
edit2.Text:=inttostr(n); //请问为啥这里返回65? 我想要的是变量a的内存地址 
end; 

procedure TForm1.Button3Click(Sender: TObject); 
var 
a:integer; 
n: Cardinal; 
begin 
a:=123; 
n:=Cardinal(a); 
edit3.Text:=inttostr(n); //请问为啥这里还是返回123? 我想要的是变量a的内存地址 
end; 

请万老师费心一下!
  回复引用
#26楼  2009-09-13 19:58 meghoo[未注册用户]
万老师,您好。
我有个问题想请教您,不知道你有没有时间给我解答?

我正在开发一个管理系统,我想使用类似WEB网页那种SESSION记录的方式来记录登陆用户或者其它一些程序关闭后即失效的东西。请问我的想法对吗?下面我说下我的想法。

由于主程序只涉及界面方面的代码,因此所有数据模块和存取操作等都是利用DLL来进行的。现在有个问题就是,有些操作需要验证使用者权限,或者检查“是否属于超级管理模式”。
以毅然是将登陆信息保存到数据库,但后来发现这种方式很不好。我想将这些数据保存到内存中(由主程序来保存)。动态链接库通过访问这个数据来确认是否已经登陆。当然为了安全,我需要做成类似WEB网页的SESSION那种session('loginname')='xxxx',的样子。我知道session可以弄成一个函数,来获得''中间的loginname变量的值。这应该不难。只是对指针不了解,想您给我讲解一下,指针的几个表示方式的意思。比如:^p,p^这两个各表示什么?是否在前面加一个p的类型都是该类型的指针类型?pinteger是一个指针类型,它读取内存数据是以integer类型来读取的,这样理解对不?

谢谢。
  回复引用
#27楼  2010-05-03 11:44 ymg1103  
万一老师,以下两个值为什么不一样,能解释一下吗?(粗体部分)
procedure TForm1.Button1Click(Sender: TObject);
var s:string;
begin
s:='abcdef';
showmessage(inttostr(integer(s))); {4542020}
showmessage(inttostr(integer(@s[1]))); {9779988}
showmessage(inttostr(integer(@s))); {1242504}
end;
  回复引用
#28楼[楼主]  2010-05-03 15:23 万一  
@ymg1103
获取一个字符串的地址用 @str
获取字符串第一个字符的地址用 @str[1]

还是尽量少用 Integer(str) 吧.
当我们通过类型转换来获取字符串的地址时, 如 PByte(str); 此时编译器会有一些协调工作.
你上面的例子稍作变换都能得到你想要的结果, 如:
1、把字符串定义成全局
2、尝试其他方式的强制转换, 如 PByte
3、把你的 Integer(s) 先给一个整数变量
但这些都很抽象, 所以只用 @str 和 @str[1] 足够了.
  回复引用
#29楼  2011-12-02 09:14 hpking  
指针与win的handle还不一样,文章说直接操作内存,那是对win相当的不了解,毕竟现在不是dos的原始年代

 

 

参考:http://www.cnblogs.com/del/archive/2008/03/07/1094655.html

posted @ 2013-03-27 18:33  bizhu  阅读(571)  评论(0编辑  收藏  举报