delphi-如何在常量记录中声明并使用指向常量数组的指针?

delphi-如何在常量记录中声明并使用指向常量数组的指针?,delphi,arrays,pointers,Delphi,Arrays,Pointers,我有几个基本类型相同但大小不同的常量数组,我需要在常量记录中指出它们。下面的代码编译成功,但完成时出错 type Toffsets = array of integer; Trec = record point1: Tpoint; //complete size point2: Tpoint; aOffsets: ^Toffsets; end; const cO

我有几个基本类型相同但大小不同的常量数组,我需要在常量记录中指出它们。下面的代码编译成功,但完成时出错

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];
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;
这会导致错误-“访问冲突…”。。。读取地址000000…'

有人能解释这里的错误以及如何修复它,应该如何做吗?

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

致以最良好的祝愿,
LUK

看来我自己找到了答案:

type
  Toffsets = array of integer;
是动态数组类型,setLength必须与此类型的任何变量一起使用以分配内存。我需要的是一个指向现有常量数组的指针,所以为了解决这个问题,唯一必须更改的就是这个类型的声明。它应该是这样的:

type
  Toffsets = array [0..0] of integer;
是的,我必须在记录中添加_length字段,因为low(pMyRec^.aOffsets^)、high(…)和length(…)不起作用

致以最良好的祝愿, LUK

以下方法应该有效:

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

i:=toffset(pMyRec^.aoffset)[0];

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

因此,记录将获得一个构造函数,该构造函数将用适当的数据填充它。缺点是,如果要使用构造函数填充数据,就不能再将其声明为const。但是,您可以在初始化部分使用以下代码来解决此问题:

var
  cRec1: Trec;

initialization
  cRec1 := Trec.Create(140, 6, 5, 7, cOffsetsA);
此代码将记录声明为全局变量而不是常量,并用数据填充它

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

var pMyRec: ^Trec;
...
pMyRec := @cRec1;
...
i := pMyRec^.aOffsets^[0];
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;
这将用您的数据填充记录。但是,您不必使用构造函数以这种方式创建记录!这只是一个附加功能

为了使它更有趣,您可以为两点和数组添加属性,使字段本身成为私有字段,并且只为属性声明read方法。这样,您仍然可以确保它的内容是只读的并且保持只读

但是使用你的代码有什么风险呢?好吧,因为您将它们都声明为常量,所以没有太大的风险,除非您允许可分配的类型化常量。({$J+}被声明…)在这种情况下,一段代码可以更改cOffsetsA中的值,也可以更改cRec1中的值。这就是你想要的吗

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


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

谢谢您的回复。这是不同的方法。我将保留代码的原样,主要是因为它可以工作。我的点评是:1。我使用Delphi6:)2。在我的实际代码中,记录中有更多的数据字段,因此将它们全部传递给构造函数不是一个好主意。3.我不喜欢相同数据的两个副本(一个在内存中,一个在代码中),但这可能是因为我编写了很多嵌入式软件,资源有限。您好,我有好的观点。:-)不过,这对其他人来说仍然是有用的信息。您似乎有充分的理由使用此方法,所以这很好。我强烈质疑
aOffsets
是否真的需要作为指向动态数组的指针。我怀疑这只是为了让问题中的(错误的)代码得以编译,一个普通的动态数组就足够了两个好点。我将aOffset设置为一个指针,使其与Q代码类似。像我那样复制数组元素也可以通过几十种方式进行优化,但我的示例也可以在结构完全不同的情况下使用。默认情况下,我编写的代码与示例类似。当代码运行时,我开始考虑优化。循环在启动期间只运行一次,因此如果幸运的话,在整个过程中,优化循环将获得约1毫秒的时间。这不是一个很大的增益…实际上[0..0]是一个坏主意,因为它迫使您关闭范围检查。使上界类似于“(MaxInt div SizeOf(Integer))-1”应该可以更好地工作。TPointerList=指针的数组[0..MaxListSize-1];MaxListSize=Maxint div 16不,它不应该工作。您正在键入一个指向toffset的指针,以告诉编译器它实际上是一个toffset。事实上,储存在那里的东西两者都没有。它是指向四个整数数组的指针。TOffsets是一个动态数组,与非动态数组不同。@Rob-我倾向于同意你的观点,也许它不应该工作。但它确实有效。字段声明中的错误与字段初始化中的错误实际上相互抵消,使错误的类型转换看起来有效。根据声明,aOffsets指向动态数组,但初始化使其指向静态数组的第一个元素。类型转换告诉编译器它指向动态数组的第一个元素。当启用范围检查时,代码将失败,因为静态数组没有长度字段。为什么有人会关闭范围检查?意外运行的错误代码仍然是错误的代码。让我们面对现实,这些记录应该是对象!如果我去他所在的地方,我就不会从这里开始(我想:)我同意它是静态和动态数组的混合体。它是