一个小问题: 记录(16分)

X

xbl

Unregistered / Unconfirmed
GUEST, unregistred user!
type
TA = record
A: TStringList;
B: array of Double;
C: Integer;
end;

var
X: TA;
begin
X. //这时候,后面出现提示信息: B: ^array of Double
//可我申明的 B 明明不是指针,这是为什么?
end;
 
是一个地址
 
你需要给出数组的维数(下标)
 
应该传给X.B一个地址
 
cwmdelpher,:
能不能详细一点?

Milpas:
我就是定义动态数组啊。
 
我想知道这是为什么,
我知道可以定义一种新类型来代替 array of Double ;
type
TADouble = array of Double;
TA = record
A: TStringList;
B: TADouble;
C: Integer;
end;

这样就可以了,我只是想知道为什么那样做不可以,
怎么我定义的 array of Double,会变成 ^array of Double?
 
动态数组的使用和一般数组差不多 你遇到什么问题了吗?
 
应该是动态数组的问题 动态数组在使用前所有东西都没有确定,所以它只能表示一个
地址
 
cwmdelpher:
你说的是,就是一个地址。

你来看看我这样用:
type
TA = record
B: array of Double;
C: Integer;
end;

var
X: TA;
Y: array of Double;
begin
SetLength(Y, 2);
Y[0] := 1.1;
Y[1] := 1.2;
X.B := @Y;
Edit1.Text := FloatToStr(X.B[0])
//2.637420087256E-308
Edit2.Text := FloatToStr(X.B[1])
//抱错,非法访问地址
end;

怎么Edit1,Edit2的结果不是 1.1,1.2 ?
那是什么原因?
 
改成下面的就行了
type
TA = record
B: array of Double;
C: Integer;
end;

var
X: TA;
Y: array of Double;
begin
SetLength(Y, 2);
Y[0] := 1.1;
Y[1] := 1.2;
X.B := @Y[0]
//注意此行
Edit1.Text := FloatToStr(X.B[0])

Edit2.Text := FloatToStr(X.B[1])

end;
 
qqssqqssqqss,说的对 
 
我觉得是因为 @Y不能取到它的地址,因为现在@Y还是不确定的
 
qqssqqssqqss:
这样就可以了,谢谢!

以前从来没有这样用过,原来动态数组的地址的取得是 @dd[0],
我手上的资料上介绍动态数组的不多,
谁能解释一下?

 
动态数组介绍----Delphi



自从有了动态数组,链表除了在教科书里出现外,已经很少在实际编程中被使用了,事实也是如此,数组的确比传统链表快得多,而且也方便的多。

从 Delphi4起,开始了内建各种类型的动态数组支持。但是,对我们来说动态数组支持似乎做的不够彻底,因为Delphi竟然连删除、插入、移动连续元素的函数都没有提供,让人使用起来总觉得不够爽!!! J 。作为一名程序员,我们当然要有自己解决问题的能力,下面就让我们简单介绍一下Delphi 下的动态数组。

在Delphi中,数组类型有静态数组(a : array[0..1024] of integer)、动态数组(var a : array of integer)、指针数组(即指向静态数组的指针)和开放数组(仅用于参数传递)。静态数组、指针数组有速度快的好处,动态数组有大小可变的优势,权衡之下就有了折衷的办法,那就是定义的动态数组在必要时转换为指针。

动态数组声明之后,只有下面几个函数可供操作:

1. 设置数组大小,可以任意缩减或增加数组大小

Procedure SetLength(var S
NewLength : integer);

2. 取出连续元素,复制给另一个数组变量

Function Copy(s;Index,Count : integer) : array ;

3. 取得数组大小及上下限

Function Length(s):integer;

Function High(x):integer;

Function Low(x):integer;

值得注意的是,不加const或var修饰的动态数组会被作为形参传递,而动态数组用const修饰并不意味着你不能修改数组里的元素(不信你可以字自己在程序中试试。还有一点是High函数调用了Length 函数,所以我们在获取数组上限时最好直接用 Length(s) 函数。

动态数组在内存空间中占用4个字节. 动态数组在内存中的分配表如下:

偏移量 内容

-8 32-bit 引用计数

-4 32-bit 数组长度

0..数组长度 * (元素尺寸) - 1 数组元素 元素尺寸=Sizeof(元素类型)

根据上面的分配情况,可以得到如下结果:

如果我们想要清空一个动态数组只需要把“数组长度”和“引用计数”清空即可。”引用上面的一句话就是:“权衡之下就有了折衷的办法,那就是定义的动态数组在必要时转换为指针。”下面是清空动态数组的函数:

procedure DynArraySetZero(var A);

var

P: PLongint
//占用4个字节,正好符合 32 位内存排列

begin

P := PLongint(A)
// 指向 A 的地址

Dec(P)
//P 地址偏移量是 sizeof(A),指向了数组长度

P^ := 0
// 长度清空

Dec(P)
// 指向引用计数

P^ := 0
//计数清空。

end;

上面的函数就这么简单,而且效率也非常高。

下面让我们再来看看怎样删除动态数组中的元素,函数体如下:

{************************************

A 变量类型 , elSize = SizeOf(A)

index 开始删除的位置索引 ,Count 删除的数量

****************************************}

procedure DynArrayDelete(var A
elSize: Longint
index, Count: Integer);

var

len, MaxDelete: Integer


P : PLongint
//4 个字节的长整形指针

begin

P := PLongint(A);// 取的 A 的地址

if P = nil then

Exit;

{

下面这句完全等同于 Dec(P)
len := P^ 因为 Dec(P) = Pchar(P) – 4 同样是移动4 字节的偏移量,只不过后者按字节来移动 }

len := PLongint(PChar(P) - 4)^
// 变量的长度 ,偏移量 -4

if index >= len then //要删除的位置超出范围,退出

Exit;

MaxDelete := len - index
// 最多删除的数量

Count := Min(Count, MaxDelete)
// 取得一个较小值

if Count = 0 then // 不要求删除

Exit


Dec(len, Count);// 移动到要删除的位置

MoveMemory(PChar(P)+index*elSize , PChar(P)+(index + Count)*elSize , (len-index)*elSize)
//移动内存

Dec(P)
//移出 “数组长度”位置

Dec(P)
//移出“引用计数” 位置

//重新再分配调整内存,len 新的长度. Sizeof(Longint) * 2 = 2*Dec(P)

ReallocMem(P, len * elSize + Sizeof(Longint) * 2);

Inc(P)
// 指向数组长度

P^ := len
// new length

Inc(P)
// 指向数组元素,开始的位置

PLongint(A) := P;

end;



对上面的例子,我们需要注意的是 elSize 参数 ,它必须是 SizeOf(DyArray_Name),表示元素所占用的字节数。

相信看了上面的例子后,对于动态数组的拷贝,移动想必也可以自己实现了吧 J

后续:

其实,Delphi 对许多类型的内存分配都很相似,比如 string 类型,其实它和动态数组是很相似的,我们完全可以把它拿来当成动态数组。实质上 string 是 Pchar 的简易版本。不管怎么说,了解一些内存的分配对我们这些开发人员来说还是有一些好处的
 
cwmdelpher:
谢谢,等下回去仔细看看!

现在,我又遇到一个问题,大家来看看:
type
TADouble = array of Double;
TA = record
B: TADouble;
C: Integer;
end;

var
D1: TADouble;
A1, A2: array of TA;
begin
SetLength(D1, 2);
D1[0] := 1.1;
D1[1] := 1.2;

SetLength(A1, 2)
//这里必须大于1???
A1[0].B := @D1[0];
A1[0].C := 1;

A2 := Copy(A1, Low(A1), High(A1))
//复制A1-->A2

Edit1.Text := FloatToStr(A2[0].B[0])

Edit2.Text := FloatToStr(A2[0].B[1])

end;

问题:
为什么设置A1的维数时,必须大于1?
如果设置A1的维数为1,SetLength(A1, 1)

那么,当我们将A1复制到A2 时: A2 := Copy(A1, Low(A1), High(A1))

好,现在来访问A2:
Edit1.Text := FloatToStr(A2[0].B[0])

Edit2.Text := FloatToStr(A2[0].B[1])


这样访问是错误的,报错:非法地址访问,这是为什么?
但是我将A1的维数设置为一个大于1的数 SetLength(A1, 3)

就一点问题也没有了,为什么?

 
全是动态数组惹的祸!!
var
I:array of Integer;
J: Integer;
begin
SetLength(I, 10);
for J := 0 to 9 do
I[J] := J;
ShowMessage(IntToStr(I[0]));
ShowMessage(IntToStr(I[9]));
end;
 
春意:
你说的我知道啊,
你这段代码的意思应该是说明:动态数组的下标默认从0开始。对吧?

可是我问的不是这个呀!
 
动态数组就是指针,指向一个结构
Type
UseTime:LongWOrd;使用计数
Length:LongWOrd;长度
Data:Array[0。。Length-1] Of 数组类型;
End;
 
(等下回去我再找找动态数组的资料。我对动态数组还不清楚)

wr960204:
你先测试一下我下面的代码,再看看我后面文字:
type
TADouble = array of Double;
TA = record
B: TADouble;
C: Integer;
end;

var
D1: TADouble;
A1, A2: array of TA;
begin
SetLength(D1, 2);
D1[0] := 1.1;
D1[1] := 1.2;

SetLength(A1, 2)
//这里必须大于1???
A1[0].B := @D1[0];
A1[0].C := 1;

A2 := Copy(A1, Low(A1), High(A1))
//复制A1-->A2

Edit1.Text := FloatToStr(A2[0].B[0])

Edit2.Text := FloatToStr(A2[0].B[1])

end;

问题:
为什么设置A1的维数时,必须大于1?
如果设置A1的维数为1,SetLength(A1, 1)

那么,当我们将A1复制到A2 时: A2 := Copy(A1, Low(A1), High(A1))

好,现在来访问A2:
Edit1.Text := FloatToStr(A2[0].B[0])

Edit2.Text := FloatToStr(A2[0].B[1])


这样访问是错误的,报错:非法地址访问,这是为什么?
但是我将A1的维数设置为一个大于1的数 SetLength(A1, 3)

就一点问题也没有了,为什么?
 
type
TADouble = array of Double;
TA = record
B: TADouble;
C: Integer;
end;

var
D1: TADouble;
A1, A2: array of TA;
begin
SetLength(D1, 2);
D1[0] := 1.1;
D1[1] := 1.2;

SetLength(A1, 1)
//这里不必须大于1
A1[0].B := @D1[0];
A1[0].C := 1;

A2 := Copy(A1, Low(A1), Length(A1))
//复制A1-->A2

Edit1.Text := FloatToStr(A2[0].B[0]);
Edit2.Text := FloatToStr(A2[0].B[1]);
end;
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
1K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
967
SUNSTONE的Delphi笔记
S
顶部