@、^、Addr、Pointer

Delphi(Pascal)中有几个特殊的符号,如@、^等,弄清楚这些符号的运行,首先要明白Delphi指针的一些基础知识:指针,是一个无符号整数(unsigned int),它是一个以当前系统寻址范围为取值范围的整数。指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。指针的指针就是用来存放指针所在的内存地址的。
明白了指针的基本含义,就容易理解它们之间 的区别了:

  • @XX:取变量、函数或过程XX的地址(获取指针);
  • Addr(XX):和@的作用类似,唯一的不同在于如果编译选项{$T-}没有打开,@返回的是一个通用的指针,如果编译选项打开了,@返回的是XX对应的指针,但Addr却不受此编译选项的约束。
  • ^:当它出现在类型定义的前面时如 ^typename 表示指向这种类型的指针; 当它出现在指针变量后边时 如 point^ 返回指针指向的变量的值;
  • Pointer:无类型指针(对应PChar、PInteger等则为“有类型指针”)。

通过这段代码则更容易区分它们:

  1. var
  2. X, Y: Integer;  // X and Y 整数类型
  3. P: ^Integer;  // P 指向整数类型的指针
  4. begin
  5. X := ;  // 给 X 赋值
  6. P := @X;  // 把 x的地址赋给p
  7. Y := P^;  // 取出p所指向的数值赋给y
  8. end;

第二行定义了两个变量X,Y。 第三行声明了P是指向整数类型的指针;意味着P能够指向X或者Y的地址。第五行赋给X值,第六行把X的地址赋给P。最后通过P指向的变量赋值给Y。此时,X和Y有相同的值。

Char、Byte

  • Char是一个字符,必须赋以字符如‘A’等;
  • Byte是无符号整数,数值范围0~255。

虽然字符实质上也是整数,但与C不同,Delphi中将他们划为两种不同的类型,各自遵从不同的运算。比如运算符+对于Byte是整数加法,对于Char则是字符连接成串。
他们可以相互转化:Ord(‘A’)得到字符对应的整数,Chr(65)得到整数对应的字符。
同理,就很好区分array of Byte和array of Char了。

Move、CopyMemory

Move字面意思上是“移动”的意思,其实不然,在Delphi中Move更像是Copy:它可以复制一段内存片段到另外一段内存空间中。如下代码:

  1. var
  2.   source, dest : string;
  3. begin
  4.   // Set up our starting string
  5.   source := '';
  6.   dest   := '---------';
  7.  
  8.   // Copy a substring from source into the middle of dest
  9.   Move(source[], dest[], );
  10.  
  11.   // Show the source and destination strings
  12.   ShowMessage('Source = '+source);
  13.   ShowMessage('Dest   = '+dest);
  14. end;
  15. //结果------------------
  16. //Source = 123456789
  17. //Dest   = --5678---
  1. //而CopyMemory则可以在Delphi的源码中看出端倪
  2. procedure CopyMemory(Destination: Pointer; Source: Pointer; Length: DWORD);
  3. begin
  4.   Move(Source^, Destination^, Length);
  5. end;

可以看出,CopyMemory其实也是调用了Move方法,但参数变了,CopyMemory参数是指针。当然,从源码还可以发现MoveMemory和CopyMemory是一模一样的功能。
CopyMemory一般的使用方法为:

  1. var
  2.   buf1,buf2: array[..] of AnsiChar;
  3. begin
  4.   buf1 := '';
  5.   buf2 := 'abcdefghij';
  6.   CopyMemory(@buf2[], @buf1[], );
  7.   ShowMessage(buf1); {}
  8.   ShowMessage(buf2); {ab45678hij}
  9. end;

GetMem和FreeMem、GetMemory和FreeMemory、New和Dispose、StrAlloc和StrDispose、AllocMem

Delphi中的内存申请和释放方法比较多,但有一点儿需要牢记的是上述方法建议配对使用。
GetMem和FreeMem与GetMemory和FreeMemory在Delphi的源码中可以看到是被调用和调用的关系,FreeMemory会判断指针是否为空:

  1. function GetMemory(Size: Integer): Pointer; cdecl;
  2. begin
  3.   Result := MemoryManager.GetMem(Size);
  4. end;
  5.  
  6. function FreeMemory(P: Pointer): Integer; cdecl;
  7. begin
  8.   if P = nil then
  9.     Result :=
  10.   else
  11.     Result := MemoryManager.FreeMem(P);
  12. end;

因此,建议用GetMemory和FreeMemory代替GetMem和FreeMem。
New和Dispose是用来管理变体类型内存分配,如变体结构体:

  1. TRecord = record
  2. Text: string;
  3. Value: Integer;
  4. end;
  5. PRecord = ^TRecord;

如果用GetMem和FreeMem、GetMemory和FreeMemory来释放,会造成Text的内存没有释放,造成内存泄漏。如果用Dispose来释放指针,要加上定义信息,否则造成内存泄漏,正确写法Dispose(PRecord(Point))。另外需要注意的一点是:Delphi设计的Dispose释放内存时,只是标记这部分内存可以再用来被New等函数分配,并不是把从系统申请到的内存归还给操作系统,只在程序结束时,才全部释放给操作系统,因此并不能在资源管理器中看到Dispose的“显著效果”。
一般使用方法如下:

  1. Type
  2.  PMyRec = ^TMyRec;
  3.  TMyRec = record
  4.   FName: string;
  5.   LName: string;
  6.  end;
  7. var
  8.  MyRecPtr: PMyRec;
  9.  TreeViewIndex: LongInt;
  10. begin
  11.  New(MyRecPtr);
  12.  MyRecPtr^.FName := Edit1.Text;
  13.  MyRecPtr^.LName := Edit2.Text;
  14.  {其他处理}
  15.  Dispose(MyRecPtr);
  16. end;

StrAlloc和StrDispose这个函数也是一对,他们分配PChar加一个Cardinal长度,因此一定要用StrDispose释放,否则容易造成4字节的内存泄漏。StrAlloc分配的指针可以使用StrBufSize来获得大小。
AllocMem和GetMem的区别在于AllocMem在申请内存后会初始化这段内存(把内存全部初始化为#0),同样和FreeMem配对使用。

Delphi中那些容易混淆的基础的更多相关文章

  1. Delphi中那些容易混淆的基础(@、^、Addr、Pointer,Move、CopyMemory,GetMem和FreeMem、GetMemory和FreeMemory、New和Dispose、StrAlloc和StrDispose、AllocMem)

    @.^.Addr.Pointer Delphi(Pascal)中有几个特殊的符号,如@.^等,弄清楚这些符号的运行,首先要明白Delphi指针的一些基础知识:指针,是一个无符号整数(unsigned ...

  2. 关于 Delphi 中的Sender和易混淆的概念(转)

    /////////////////////////////////////////////////////// Delphi 中Sender对象的定义///////////////////////// ...

  3. Delphi中的基础数据类型

    参考http://www.cnblogs.com/del/archive/2007/12/04/982167.html 在学习之初,在这么多的数据类型中,最好记住这五种标准数据类型(整型.实型.字符型 ...

  4. DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)

    函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...

  5. delphi 中几种多线程操作方式

    在了解多线程之前我们先了解一下进程和线程的关系 一个程序至少有一个主进程,一个进程至少有一个线程. 为了保证线程的安全性请大家看看下面介绍 Delphi多线程同步的一些处理方案大家可以参考:http: ...

  6. Delphi 中的DLL 封装和调用对象技术(刘艺,有截图)

    Delphi 中的DLL 封装和调用对象技术本文刊登2003 年10 月份出版的Dr.Dobb's 软件研发第3 期刘 艺摘 要DLL 是一种应用最为广泛的动态链接技术但是由于在DLL 中封装和调用对 ...

  7. C#中一些易混淆概念总结

    C#中一些易混淆概念 这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框 ...

  8. DELPHI中多线程知识【转】

    本文的内容取自网络,并重新加以整理,在此留存仅仅是方便自己学习和查阅.所有代码均亲自测试 delphi7下测试有效.图片均为自己制作. 多线程应该是编程工作者的基础技能, 但这个基础我从来没学过,所以 ...

  9. Delphi中根据分类数据生成树形结构的最优方法

    一. 引言:    TreeView控件适合于表示具有多层次关系的数据.它以简洁的界面,表现形式清晰.形象,操作简单而深受用户喜爱.而且用它可以实现ListView.ListBox所无法实现的很多功能 ...

随机推荐

  1. IDEA导入tomcat9源码跑起来~

    如题,这里记录一下用IDEA导入tomcat9的源码,并跑起来.看了本教程你还是不会的话直接问我. 一.环境安装以及目录搭建 tomcat9源码下载地址:http://mirrors.hust.edu ...

  2. struts2中在Action中如何获取servlet的api?

    1.通过ActionContext类(拿到的不是真正的servlet api,而是一个map) ActionContext context = ActionContext.getContext(); ...

  3. 更改Mysql 密码的4种方法(转)

    原文:http://www.jb51.net/article/39454.htm 方法1: 用SET PASSWORD命令 首先登录MySQL. 格式:mysql> set password f ...

  4. pycharm中不能安装bs4的解决方案

    首先:什么Beautiful Soup? Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.B ...

  5. python 基础 9.11 更改数据

    #/usr/bin/python #-*- coding:utf-8 -*- #@Time   :2017/11/24 4:45 #@Auther :liuzhenchuan #@File   :更改 ...

  6. SharePoint解决方案及开发系列(1)-BPM

    自从2008年做SharePoint第一个项目至今,不知不觉已经快7个年头了:上次听涂曙光老师的讲座,有机会能跟他面对面地沟通(“我是看您的blog长大的”).刚换了新工作,暂时比较闲,乘着这段时间对 ...

  7. Spring标签@Aspect-实现面向方向编程(@Aspect的多数据源自动加载)——SKY

    从Spring 2.0开始,可以使用基于schema及@AspectJ的方式来实现AOP.由于@Aspect是基于注解的,因此要求支持注解的5.0版本以上的JDK. 环境要求:    1. mybit ...

  8. config相关操作(转)

    转自:http://www.cnblogs.com/kissdodog/archive/2013/04/16/3025315.html,这是一个专题,感觉比较好,有空可以看与一下 System.Con ...

  9. ls --color=xxx

      默认的ls是由"ls --color=auto"组成的,假如某个目录中的文件特别多,我不希望显示颜色(可以加快显示),那就需要指定单独的参数. [root@localhost ...

  10. 关于Future

    1 为什么需要Callable和Future Runnable没有返回值,也不抛异常,这样主线程不能知道子线程的执行结果. 为了解决这个问题就有了Callable和Future.Callable提供的 ...