【问题标题】:delphi - how to use declare and use pointer to the const array in a const record?delphi - 如何在 const 记录中使用声明和使用指向 const 数组的指针?
【发布时间】:2009-10-07 08:58:21
【问题描述】:

我有几个基本类型相同但大小不同的 const 数组,我需要在 const 记录中指向它们。下面的代码编译成功,但以错误结束。

type
  Toffsets = array of integer;

  Trec = record
             point1: Tpoint;          //complete size
             point2: Tpoint;
             aOffsets:  ^Toffsets;
           end;

const
  cOffsetsA: array [0..3] of integer = (7, 4, 2, 9);
  cOffsetsB: array [0..5] of integer = (1, 2, 3, 4, 5, 6);

  cRec1: Trec = (
    point1:   (x: 140; y: 46);
    point2:   (x: 5; y: 7);
    aOffsets: @cOffsetsA;
  );

  cRec2: Trec = (
    point1:   (x: 40; y: 6);
    point2:   (x: 5; y: 7);
    aOffsets: @cOffsetsB;
  );

在我的代码中,我需要访问具有指向记录的指针的 cOffsetsA/B 数组中的数据。我试着这样做:

var pMyRec: ^Trec;
...
pMyRec := @cRec1;
...
i := pMyRec^.aOffsets^[0];

这会导致错误 - '访问冲突...读取地址 000000...'

谁能解释一下这里有什么问题以及如何解决它,应该怎么做?

可能我还必须在记录中添加 _length 字段,该字段将保存指针指向的数组的大小;这不是问题。

最好的问候, 卢克

【问题讨论】:

    标签: delphi arrays pointers


    【解决方案1】:

    虽然在 Delphi 中可以使用这样的常量,但我只想补充一点,通常以这种方式做事并不是一个好主意。就个人而言,如果此代码在某个单元中,我会使用如下代码:(D2007 及更高版本)

    type
      Toffsets = array of integer;
      Trec = record
        point1: Tpoint; //complete size
        point2: Tpoint;
        aOffsets: ^Toffsets;
        constructor Create(X1, Y1, X2, Y2: Integer; Offsets: array of integer);
      end;
    

    因此,记录获得了一个构造函数,它将用适当的数据填充它。作为一个缺点,如果你想使用构造函数来填充数据,你不能再将它声明为一个常量。但是,您可以通过在初始化部分中使用以下代码来解决此问题:

    var
      cRec1: Trec;
    
    initialization
      cRec1 := Trec.Create(140, 6, 5, 7, cOffsetsA);
    

    此代码会将记录声明为全局变量而不是常量,并用您的数据填充它。

    您的构造函数可能如下所示:

    constructor Trec.Create(X1, Y1, X2, Y2: Integer; Offsets: array of integer);
    var
      I: Integer;
    begin
      point1.X := X1;
      point1.Y := Y1;
      point2.X := X2;
      point2.Y := Y2;
      new(aOffsets);
      SetLength(aOffsets^, 0);
      for I in Offsets do
      begin
        SetLength(aOffsets^, Succ(Length(aOffsets^)));
        aOffsets^[Pred(Length(aOffsets^))] := I;
      end;
    end;
    

    这将用您的数据填充记录。但是,您不必以这种方式使用构造函数来创建记录!这只是一个附加功能。

    为了更有趣,您可以为两个点和数组添加属性,将字段本身设为私有,并且只为属性声明读取方法。这样,您仍然可以确保其内容是只读的并保持只读状态。

    但是使用您的代码有什么风险?好吧,由于您将它们全部声明为常量,因此风险不大,除非您允许可分配的类型常量。 (声明了 {$J+}...)在这种情况下,一段代码可以更改 cOffsetsA 中的值,并且它也会更改 cRec1 中的值。那是你要的吗?

    无论如何,通过使用构造函数并在代码中初始化它们,您可以使代码更加动态。但是,它仍然是一个简单的记录类型,附加的属性和方法不会改变它的结构或大小。

    不过,您需要 Delphi 2007 或更高版本...

    【讨论】:

    • 感谢您的回复。这是不同的方法。我将保留代码原样,主要是因为它可以工作。我的小 cmets 是: 1. 我使用 Delphi 6 :) 2. 在我的真实代码中,记录中有更多的数据字段,因此将它们全部传递给构造函数不是一个好主意。 3. 我不喜欢有两份相同的数据(一份在内存,一份在代码),但这可能是因为我写了很多嵌入式软件,资源有限。问候,卢克
    • 好点。 :-) 不过,它对其他人来说仍然是有用的信息。您似乎有正当理由使用此方法,所以没关系。
    • 我强烈质疑aOffsets 是否真的需要成为一个指向动态数组的指针。我怀疑它只是以这种方式声明以允许编译问题中的(错误)代码,并且普通的动态数组就足够了。
    • 与其一次扩展和复制一个数组元素,不如在一个语句中复制整个数组:aOffsets^ := Copy(Offsets);
    • TGwo 好点。我将 aOffset 设为指针以使其与 Q 代码相似。像我一样复制数组元素也可以通过几十种方式进行优化,但是当结构完全不同时也可以使用我的示例。默认情况下,我像示例一样编写代码。当代码工作时,我开始考虑优化。该循环仅在启动期间运行一次,因此如果幸运的话,在整个过程中优化它将获得大约 1 毫秒。收益不大……
    【解决方案2】:

    看来我自己找到了答案:

    type
      Toffsets = array of integer;
    

    是动态数组类型,setLength 必须与该类型的任何变量一起使用以分配内存。我需要的是一个指向现有常量数组的指针,所以为了解决这个问题,唯一必须改变的是这种类型的声明。它应该看起来像:

    type
      Toffsets = array [0..0] of integer;
    

    是的,我必须在记录中添加 _length 字段,因为 low(pMyRec^.aOffsets^)、high(...) 和 length(...) 不起作用。

    最好的问候, 卢克

    【讨论】:

    • 实际上 [0..0] 是个坏主意,因为它会强制您关闭范围检查。将上限设置为“(MaxInt div SizeOf(Integer))-1”应该会更好。
    • 看看 TList 等的代码 - 它做了类似的事情。 TPointerList = 数组[0..MaxListSize - 1] 的指针; MaxListSize = Maxint div 16
    【解决方案3】:

    以下应该有效:

    
    i:=TOffsets(pMyRec^.aOffsets)[0];
    

    【讨论】:

    • 不,它不应该工作。您正在对指向 TOffsets 的指针进行类型转换,以告诉编译器它实际上是一个 TOffsets。事实上,存储在那里的东西都不。它是一个指向四整数数组的指针。 TOffsets 是动态数组,与非动态数组不同。
    • @Rob - 我有点同意你的观点,也许它不应该工作。但它确实有效。
    • 字段声明中的错误与字段初始化中的错误实际上相互抵消,使您错误的类型转换出现起作用。根据声明,aOffsets 指向动态数组,但初始化使其指向静态数组的第一个元素。类型转换告诉编译器它指向动态数组的第一个元素。启用范围检查时代码将失败,因为静态数组没有长度字段。为什么有人会关闭范围检查?意外工作的错误代码仍然是错误代码。
    • 让我们面对现实吧,这些记录应该是对象!如果我要去他所在的地方,我不会从这里开始:)
    • 我同意它混合了静态和动态数组。这不是我的做法。 RE 范围检查 - 在 Delphi(至少为 delphi 6)中,范围检查默认禁用。来自 HELP 的引用:“启用范围检查会减慢您的程序并使其变得更大,因此仅将 {$R+} 用于调试。”。
    猜你喜欢
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    • 2011-01-13
    • 1970-01-01
    相关资源
    最近更新 更多