当前位置:  开发笔记 > 开发工具 > 正文

在Delphi中使用指针

如何解决《在Delphi中使用指针》经验,为你挑选了2个好方法。

我已经开发了一段时间了,到目前为止我还没有在开发过程中使用指针.

那么指针的好处是什么?应用程序运行得更快还是使用更少的资源?

因为我确信指针很重要,你可以"指出"一些文章,基本但很高兴开始在Delphi中使用指针吗?谷歌给了我太多,太特别的结果.



1> Toon Krijthe..:

指针是指向一块内存的变量.优点是:

你可以给你想要的那块内存.

您只需将指针更改为指向不同的内存,这样可以节省大量的复制时间.

Delphi使用了很多隐藏指针.例如,如果您使用:

var
  myClass : TMyClass;
begin
  myClass := TMyClass.Create;

myClass是指向对象的指针.

另一个例子是动态数组.这也是一个指针.

要了解有关指针的更多信息,您需要了解有关内存的更多信息.每条数据都可以存在于不同的数据中.

例如全局变量:

unit X;

interface

var
  MyVar: Integer;

全局变量在数据段中定义.数据段是固定的.在程序的生命周期中,这些变量是可用的.这意味着内存不能用于其他用途.

局部变量:

procedure Test;
var
  MyVar: Integer;

堆栈上存在局部变量.这是一块用于管家的记忆.它包含函数的参数(确定一些放在寄存器中但现在不重要).它包含返回地址,因此如果程序结束,cpu知道返回的位置.它包含函数中使用的局部变量.局部变量仅在函数的生命周期内存在.如果函数结束,则无法以可靠的方式访问本地变量.

堆变量:

procedure Test2;
var
  MyClass: TMyClass;
begin
  MyClass := TMyClass.Create;

变量MyClass是一个指针(它是在堆栈上定义的局部变量).通过构造一个对象,你可以在堆上分配一块内存(大块的'其他'内存,不用于程序和堆栈).变量MyClass包含这段内存的地址.堆释变量存在,直到您释放它们.这意味着如果在不释放对象的情况下退出函数Test2,则对象仍然存在于堆上.但是你将无法访问它,因为地址(变量MyClass)消失了.

最佳做法

几乎总是优选地在同一级别分配和释放指针变量.

例如:

var
  myClass: TMyClass;
begin
  myClass := TMyClass.Create;
  try
    DoSomething(myClass);
    DoSomeOtherthing(myClass);
  finally
    myClass.Free;
  end;
end;

如果可以,请尝试避免返回对象实例的函数.永远不确定调用者是否需要处理该对象.这会造成内存泄漏或崩溃.



2> mghie..:

到目前为止,您已经获得了很多好的答案,但是当您使用长字符串,动态数组和对象引用时已经处理指针的答案开始时,您应该开始想知道为什么使用指针而不是长字符串,动态数组和对象引用.有没有理由继续使用指针,因为Delphi在很多情况下很好地隐藏了它们?

让我举两个Delphi中指针使用的例子.如果您主要编写业务应用程序,您会发现这可能与您无关.但是,如果您需要使用未由任何标准Delphi单元导入的Windows或第三方API函数,并且无法找到(例如)JEDI库中的导入单元,则可能会变得非常重要.并且它可能是在字符串处理代码中实现必要的最后一点速度的关键.

指针可用于处理不同大小的数据类型(编译时未知)

考虑Windows位图数据类型.每个图像可以具有不同的宽度和高度,并且存在不同的格式,范围从黑色和白色(每像素1位)超过2 ^ 4,2 ^ 8,2 ^ 16,2 ^ 24或甚至2 ^ 32灰度值或颜色.这意味着在编译时未知位图将占用多少内存.

在windows.pas中有TBitmapInfo类型:

type
  PBitmapInfo = ^TBitmapInfo;
  tagBITMAPINFO = packed record
    bmiHeader: TBitmapInfoHeader;
    bmiColors: array[0..0] of TRGBQuad;
  end;
  TBitmapInfo = tagBITMAPINFO;

所述TRGBQuad元件描述了单个像素,但位图确实当然包含多个像素.因此,永远不会使用TBitmapInfo类型的局部变量,但始终是指向它的指针:

var
  BmpInfo: PBitmapInfo;
begin
  // some other code determines width and height...
  ...
  BmpInfo := AllocMem(SizeOf(TBitmapInfoHeader)
    + BmpWidth * BmpHeight * SizeOf(TRGBQuad));
  ...
end;

现在使用指针可以访问所有像素,即使TBitmapInfo只有一个像素.请注意,对于此类代码,您必须禁用范围检查.

这样的东西当然也可以用TMemoryStream类来处理,TMemoryStream类基本上是一个指向内存块的指针的友好包装器.

当然,简单地创建TBitmap并分配其宽度,高度和像素格式要容易得多.再说一遍,Delphi VCL确实消除了大多数需要指针的情况.

字符指针可用于加速字符串操作

这与大多数微优化一样,只有在极端情况下才能使用,在您分析并发现使用字符串的代码消耗大量时间之后.

字符串的一个很好的属性是它们是引用计数的.复制它们不会复制它们占用的内存,而只会增加引用计数.只有当代码尝试修改引用计数大于1的字符串时才会复制内存,以创建引用计数为1的字符串,然后可以安全地修改该字符串.

字符串的一个不太好的属性是它们是引用计数的.可能修改字符串的每个操作都必须确保引用计数为1,因为否则对字符串的修改将是危险的.替换字符串中的字符就是这样的修改.为了确保引用计数为1 ,只要写入字符串中的字符,编译器就会添加对UniqueString()的调用.现在写ñ字符串的字符在循环中会导致UniqueString()被调用ñ倍,即使之后的第一时间确保了引用计数为1.这意味着基本上N - 1个的通话UniqueString()执行不必要的.

使用指向字符的指针是加速涉及循环的字符串操作的常用方法.想象一下,您希望(出于显示目的)用小点替换字符串中的所有空格.使用调试器的CPU视图并比较为此代码执行的代码

procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString;
var
  i: integer;
begin
  Result := AValue;
  for i := 1 to Length(Result) do begin
    if Result[i] = ' ' then
      Result[i] := $B7;
  end;
end;

用这个代码

procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString;
var
  P: PAnsiChar;
begin
  Result := AValue;
  P := PAnsiChar(Result);
  while P[0] <> #0 do begin
    if P[0] = ' ' then
      P[0] := $B7;
    Inc(P);
  end;
end;

在第二个函数中,当第一个字符串的地址被赋给char指针时,只有一个对UniqueString()的调用.

推荐阅读
殉情放开那只小兔子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有