• Delphi 之 第八课 动态数组


    Delphi 4 的动态数组

    传统的Pascal 语言其数组大小是预先确定的,当你用数组结构声明数据类型时,你必须指定数组元素的个数。专业程序员也许知道些许动态数组的实现技术,一般是采用指针,用手工分配并释放所需的内存。

    Delphi 4中增加了非常简单的动态数组实现方法,实现过程效仿我前面讲过的动态长字符串。与长字符串一样,动态数组的内存动态分配并且引用记数,不过动态数组不支持 copy-on-write 技术。这不是个大问题,因为你可以把变量值设置为nil释放数组内存。

    这样你就可以声明一个不指定元素个数的数组,并用SetLength 过程给数组分配一个特定大小的内存,SetLength 过程还可以改变数组大小而不影响其内容,除此外还有一些字符串过程也可用于数组,如Copy 函数。

    以下摘录的代码突出了一点,这就是:定义数组后必须先为它分配内存,然后才能开始使用:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      Array1: array of Integer;
    begin
      Array1 [1] := 100; // error
      SetLength (Array1, 100);
      Array1 [99] := 100; // OK
      ...
    end;

    如果你只定义一个数组元素个数,那么索引总是从0开始。Pascal 中的普通数组既能用不为零的下标,也能用非整数的下标,但动态数组均不支持这两种下标。象普通数组一样,你可以通过Length、High和Low 函数了解到动态数组的状况,不过对于动态数组,Low 函数返回值总是0,High函数返回数组大小减1,这意味着空的动态数组其函数High返回值是-1,这是一个很怪的值,因为它比Low的返回值还小。

    图 8.1: 例 DynArr 窗体

    以上作了简短的介绍,现在举个简例,例名DynArr ,见图8.1。例子实在是很简单,其实动态数组没有什么特别复杂地方。我想通过该例说明几个程序员可能犯的错误。程序中声明了两个全程数组并在OnCreate 事件中初始化了第一个数组:

    var
      Array1, Array2: array of Integer;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      // allocate
      SetLength (Array1, 100);
    end;

    这样就把数组所有值设置为0。完成这段代码你马上就能读写数组元素的值,而不用害怕内存出错,当然条件是你没有试图访问超过数组上界的元素。为了更好地初始化,程序中添加了一个按钮,执行数组元素赋值操作:

    procedure TForm1.btnFillClick(Sender: TObject);
    var
      I: Integer;
    begin
      for I := Low (Array1) to High (Array1) do
        Array1 [I] := I;
    end;

    Grow 按钮用于修改数组大小,但并不影响数组内容。单击Grow 按钮后,你可以用Get value按钮进行检验:

    procedure TForm1.btnGrowClick(Sender: TObject);
    begin
      // grow keeping existing values
      SetLength (Array1, 200);
    end;
    
    procedure TForm1.btnGetClick(Sender: TObject);
    begin
      // extract
      Caption := IntToStr (Array1 [99]);
    end;

    Alias 按钮的OnClick 事件代码稍复杂些,程序通过 := 算子把一个数组拷贝给另一个数组,从而有效地创建了一个别名(一个新变量,但引用内存中同一数组)。从中可见,如果你改变了其中一个数组,那么另一个同样也会改变,因为它们指向同一个内存区:

    procedure TForm1.btnAliasClick(Sender: TObject);
    begin
      // alias
      Array2 := Array1;
      // change one (both change)
      Array2 [99] := 1000;
      // show the other
      Caption := IntToStr (Array1 [99]);
    

    在btnAliasClick 事件中增加了两部分操作内容。第一部分是数组等同测试,不过并不是测试实际的数组元素,而是测试数组所引用的内存区,检测变量是不是内存中同一数组的两个别名:

    procedure TForm1.btnAliasClick(Sender: TObject);
    begin
      ...
      if Array1 = Array2 then
        Beep;
      // truncate first array
      Array1 := Copy (Array2, 0, 10);
    end;

    btnAliasClick 事件的第二部分内容是调用Copy 函数。该函数不仅把数据从一个数组移到另一个数组,而且用函数创建的新数组取代第一个数组,结果变量Array1 所引用的是11个元素的数组,因此,按Get value 和Set value 按钮将产生一个内存错误,并且触发一个异常(除非你把范围检查range-checking 选项关掉,这种情况下,错误仍在但屏幕上不会显示异常)。虽然如此,Fill 按钮仍能正常工作,因为需要修改的数组元素由数组当前的下标范围确定。

    结束语

    这一章内容暂时只包括动态数组,动态数组的确是内存管理的重要组成部分,但仅仅是其中的一部分,其它内容以后会逐步添加。

    本章描述的内存结构属于典型的 Windows 编程内容,这方面内容将在下一章进行讨论。

    下一章: Windows 编程

  • 相关阅读:
    徒手画个disk不容易啊。。。
    fast powf
    SSE sqrt还是比C math库的sqrtf快了不少
    Mongoose也是个大坑
    A tiny program to benchmark image transpose algorithms
    On extracting ops from LLVM backend
    Into concurrent LRU caching once again
    性能大坑
    多项式在线拟合神器
    Spark 1.6.1源码编译
  • 原文地址:https://www.cnblogs.com/delphi2014/p/4012681.html
Copyright © 2020-2023  润新知