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指向动态数组,但初始化使其指向静态数组的第一个元素。类型转换告诉编译器它指向动态数组的第一个元素。当启用范围检查时,代码将失败,因为静态数组没有长度字段。为什么有人会关闭范围检查?意外运行的错误代码仍然是错误的代码。让我们面对现实,这些记录应该是对象!如果我去他所在的地方,我就不会从这里开始(我想:)我同意它是静态和动态数组的混合体。它是