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 标识着 ↓ 的位置:
换二进制图示一下:
↓ |
|
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楼
maxcool[未注册用户]
请教2个小小问题:
引用:1145258561 转换成十六进制是: 44434241, 写得清楚一点是: $44 $43 $42 $41; 还记得 Inter 等当下流行的 CPU 安排数据是倒着的吗?
+++
1)是Intel不应是Inter吧。
2)CPU数据倒着排,可是CPU不是内存,与内存有关系吗。
#2楼
maxcool[未注册用户]
我对指针的理解,万一老师看对不对
其实指针记录的是某个变量的数据的内存地址。
就好像是个索引。
比如图书馆有本书,拿到书的指针等于其实等于拿到那本书的具体位置,例如某排某列。而要拿到那本书,要追踪到这个指针的具体地址。换句话说,拿到指针,只拿到一张纸条(上面记录书的具体位置);要拿到书这个物品,要根据这个纸条上的地址去取。指针怎么跟藏宝地图似的呵呵。
指针的指针相当于什么,相当于这个记录书位置纸条的位置。。。。不知道会不会变态到指针的指针的指针,估计没有需要吧?
警察破案,找到张三,张三供出李四,那么张三就是李四的指针,李四供出王五,那么李四就是王五的指针。张三就是五五指针的指针。。。。。
以上理解对不,请万一老师与各位访客批评指正!
#3楼
maxcool[未注册用户]
那么我想问,为什么程序里要设置指针呢?这个问题好像很愚蠢,但是好多初学者很迷惑。。。。
#4楼 [
楼主]
万一
@maxcool:
你说的问题我改了;
关于 CPU 与内存: 内存是由 CPU 管理的, 它爱怎么用就怎么用;
你说指针就像个索引, 我觉得完全对. 但指针本身只是个编号, 它还要指向一个地址;
为什么要用指针呢? 回答: 假如你说的图书馆的书没有编号...
#5楼
maxcool[未注册用户]
这里有一篇虽然比不上万一老师的,但是也不错,与大家共享
http://hi.baidu.com/ton666/blog/item/545111016307700d7bec2c90.html
#6楼
wuzhong[未注册用户]
@maxcool:
你说的很有理,其实我个人认为可以这样理解指针.
@del:
感谢你的热诚,你分析问题的思路很清晰,也能见你的基础知识很牢固.
那我还有一个问题:
我们是不是可以把每一个变量(或者实例)都看成两个部分,
TNode=Record
Data:SomeType;
Next:Pointer;
end;
这些变量就是通过链表的方式存储在内存里(我没学过数据结构,不知道可不可以这样理解),通过压栈的方式存入到内存中(后面一句是蒙的)。
#7楼 [
楼主]
万一
@wuzhong: 我明白你的意思了.
一个变量和结构关系不大(当然也可以把结构定义成一个变量), 和链表也没有关系; 反过来关系倒是很大.
这样吧, 我再试着探讨一下结构和类的指针情况.
周一吧, 周六周日很忙.
#8楼
厨师[未注册用户]
请问万老师。数据倒着排列到底是啥原理。。。我用C32打开一个程序时候。比如有时候看到些字符。比如task....没发现数据是倒着排的呀。都是从左到右
#9楼 [
楼主]
万一
@厨师:
譬如一个整数: 四字节的整数 2864434397, 它的四个字节分别是:
$AA $BB $CC $DD; 但这只是我们的思维方式, 在 Intel CPU 会这样管理它们: $DD $CC $BB $AA
至于不管用什么工具观察到的, 如果不是这样, 那是工具原因.
#10楼
厨师[未注册用户]
还记得 Intel 等当下流行的 CPU 安排数据是倒着的吗?
//万老师能详细教教这句话如何理解么。。。数据倒着排列是啥意思啊。OD打开的程序都是倒着排列啊?
#11楼
厨师[未注册用户]
老师还有几个疑问啊
1,。。ShowMessage(PChar(n)); {ABCDE}
为什么不是pchar(n)^ 后几个都是用了^啊
2,
为什么最后一个是倒着排列 为什么最后一个才提到CPU倒着排列数据,而前几个也没牵扯到这个问题
#12楼 [
楼主]
万一
@厨师:
譬如一个四字节的整数 1145258561, 它的十六进制表示是: $44434241, 每个字节分别是: $44 $43 $42 $41, 但 Inter CPU 是这样安排它们的: $41 $42 $43 $44, 这就是我说的 "倒" 着.
关于 "为什么不是pchar(n)^ 后几个都是用了^啊?"
因为后面几个都是定义的指针类型, 譬如: pb: PByte; pint: PInteger;
#13楼
厨师[未注册用户]
但指针本身只是个编号, 它还要指向一个地址;
//这句我是这样理解的。指针编号本身就是个地址。指针其实可以理解为就是一个指针变量。这个变量存储的数据是另一个变量的地址! 不知道这样理解对不老师。指针是确实存在的一个东西。不是一个抽象的东西! 对不
#14楼 [
楼主]
万一
我觉得你理解的对.
我的理解是: 系统用链表来管理指针, 这个链表中的元素起码有 -- 指针、指针类型、执向的地址.
#15楼
duoluo[未注册用户]
老师我有问题请教
我要读写某程序的数据,已经知道了在数据段的内存地址
我想问读写数据的时候指针的类型是不是必须和数据的类型一致
#17楼
Delphier[未注册用户]
--引用--------------------------------------------------
万一: @厨师:
<br>譬如一个四字节的整数 1145258561, 它的十六进制表示是: $44434241, 每个字节分别是: $44 $43 $42 $41, 但 Inter CPU 是这样安排它们的: $41 $42 $43 $44, 这就是我说的 "倒" 着.
<br>
<br>关于 "为什么不是pchar(n)^ 后几个都是用了^啊?"
<br>因为后面几个都是定义的指针类型, 譬如: pb: PByte; pint: PInteger;
<br>
--------------------------------------------------------
譬如一个四字节的整数 1145258561,这不是五个字节吗?晕了
#18楼 [
楼主]
万一
@Delphier
这里的 1145258561 是十进制.
#19楼
下饭菜
我一直有个疑问:delphi中的string类型变量是不是指针,如果是指针,如何获得指针中保存的地址?
#20楼
下饭菜
如果不是指针,那么第一例中:
n := Cardinal(str); {获取内存地址}
pstr := @str; {现在 pstr 是 str 的指针}
{n 与 pstr 的数字结果是(结果是随机的, 知道不一样就行了):}
ShowMessage(IntToStr(n)); {4571092}
ShowMessage(IntToStr(Cardinal(pstr))); {1244652}
两个内存地址就应该一样
#21楼
下饭菜
能够解释一下第一例中两个地址的含义么?到底是4571092代表了“ABCDE”的真实地址,还是1244652?另一个又代表什么?
#22楼 [
楼主]
万一
@下饭菜
一个是字符串的起点位置, 一个是指向这个位置的指针.
#23楼
dragon10001[未注册用户]
万老师,这里可不可以这样理解,4571092代表了“ABCDE”的真实地址,1244652代表了指向这个位置的指针的地址,ShowMessage(IntToStr(Cardinal(pstr)))中的(Cardinal(pstr)可以看做是取指针的地址,和n := Cardinal(str)是相同的,是不是这样?
#24楼
小亮子
万一老师:
你好!
在您的博客上学到的东西比看书真是实在!
上面的例子我在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楼
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楼
meghoo[未注册用户]
万老师,您好。
我有个问题想请教您,不知道你有没有时间给我解答?
我正在开发一个管理系统,我想使用类似WEB网页那种SESSION记录的方式来记录登陆用户或者其它一些程序关闭后即失效的东西。请问我的想法对吗?下面我说下我的想法。
由于主程序只涉及界面方面的代码,因此所有数据模块和存取操作等都是利用DLL来进行的。现在有个问题就是,有些操作需要验证使用者权限,或者检查“是否属于超级管理模式”。
以毅然是将登陆信息保存到数据库,但后来发现这种方式很不好。我想将这些数据保存到内存中(由主程序来保存)。动态链接库通过访问这个数据来确认是否已经登陆。当然为了安全,我需要做成类似WEB网页的SESSION那种session('loginname')='xxxx',的样子。我知道session可以弄成一个函数,来获得''中间的loginname变量的值。这应该不难。只是对指针不了解,想您给我讲解一下,指针的几个表示方式的意思。比如:^p,p^这两个各表示什么?是否在前面加一个p的类型都是该类型的指针类型?pinteger是一个指针类型,它读取内存数据是以integer类型来读取的,这样理解对不?
谢谢。
#27楼
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楼 [
楼主]
万一
@ymg1103
获取一个字符串的地址用 @str
获取字符串第一个字符的地址用 @str[1]
还是尽量少用 Integer(str) 吧.
当我们通过类型转换来获取字符串的地址时, 如 PByte(str); 此时编译器会有一些协调工作.
你上面的例子稍作变换都能得到你想要的结果, 如:
1、把字符串定义成全局
2、尝试其他方式的强制转换, 如 PByte
3、把你的 Integer(s) 先给一个整数变量
但这些都很抽象, 所以只用 @str 和 @str[1] 足够了.
#29楼
hpking
指针与win的handle还不一样,文章说直接操作内存,那是对win相当的不了解,毕竟现在不是dos的原始年代
参考:http://www.cnblogs.com/del/archive/2008/03/07/1094655.html
引用:1145258561 转换成十六进制是: 44434241, 写得清楚一点是: $44 $43 $42 $41; 还记得 Inter 等当下流行的 CPU 安排数据是倒着的吗?
+++
1)是Intel不应是Inter吧。
2)CPU数据倒着排,可是CPU不是内存,与内存有关系吗。