动态指针数组。。。。。

mdejtod 2009-03-11 08:59:21
RT,有这样的定义
type
TPointData = array of Pointer;
end;

var FData : TPointerData;
i : integer;

在程序里面动态添加数组元素

setlength(Fdata,length(FData) + 1);
FData[length(FData) - 1] := pointer(i);

如果是整形的则没问题,之后可以正常访问FData[i]的值

如果是将字符串赋给指针则之后出现乱码
var str : string;
setlength(Fdata,length(FData) + 1);
FData[length(FData) - 1] := pointer(str);
在程序段中showmessage(string(FData[length(FData) - 1]));是正常的,但我在其它地方用到这个数组时,就出现乱码

如果是静态数组,则直接
new(Fdata[i]);
然后赋值。。。

搞得我都糊涂了,。。。具体要怎么做才能正常显示???我在动态数组中NEW()会出错,不知道为什么。。。。
...全文
585 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
雷布斯尼亚 2009-03-17
  • 打赏
  • 举报
回复
mark
mdejtod 2009-03-12
  • 打赏
  • 举报
回复
-_-
1.那这种操作是不是有必要的?
var FarrayVariant : array of pointer;
For i := 0 to 1o do
begin
DriveString := '测试' + inttostr(i);
SetLength(FarrayVariant,Length(FarrayVariant) +1 );
FarrayVariant[Length(FarrayVariant) -1 ] := strNew(PChar(DriveString)) ;
end;
有没有必要进行手动释放?
For i := 0 to 1o do
begin
StrDispose(Pcahr(FarrayVariant[i]));
end;

2.对于对象实例来说,obj实际上是一个有类型的指针,所以
var
obj: TObject;
p: Pointer;
begin
obj := TObject.Create;
try
p := obj; // p和obj指向了同样的地址,使用TObject(p)和obj效果相同
finally
FreeAndNil(obj); // 现在p所指向的内存已经被释放,再使用p可能会导致内存访问错误。 那如果不释放这个对象,在其它地方访问是不是正常的?
end;
end;

3.我释放前面申请的内存,并把数组长度设为0 在任务管理器里看不到内存使用的变化呢,。。。
ZuoBaoquan 2009-03-12
  • 打赏
  • 举报
回复
1. 对于对象实例来说,obj实际上是一个有类型的指针,所以
var
obj: TObject;
p: Pointer;
begin
obj := TObject.Create;
try
p := obj; // p和obj指向了同样的地址,使用TObject(p)和obj效果相同
finally
FreeAndNil(obj); // 现在p所指向的内存已经被释放,再使用p可能会导致内存访问错误。
end;
end;

严格的来说,声明指针变量时,编译器已经为这个指针变量*本身*分配了内存。给这个指针赋值时(p:=obj;),实际上是把obj所引用的对象实例的地址保存到这个指针变量内。(呵呵,也就是说:指针变量的值保存的是某个对象实例的地址)。

但字符串和对象不同(由于字符串由Delphi自动管理创建和释放,引用计数为0时自动释放),需要分配内存:
procedure Test;
var
str: string;
p: PString; // 声明为PString,可以利用string的引用计数;如果为PChar,使用StrNew,则会重新拷贝内容。
begin
str := IntToStr(12345); // 此时字符串的引用计数为1
New(p);
p^ := str; // 此时字符串的引用计数为2
Dispose(p); // 此时字符串的引用计数为1
end; // 离开作用域后字符串引用计数为0,自动释放内存。

2. 内存没有变化是指哪块内存?调用Dispose或StrDispose只负责释放指针所指向的那块内存,并没有把指针置为nil,如果再使用这个指针,*可能*会导致无效访问(当然,因为长字符串的内容分配在堆上,当这块内存还没有被覆盖时,数据还是正确的,但这块内存区域应该已经标记为不可用了(个人猜测))。

3. 前面已经说了,使用无类型的Pointer,无法知道元素的实际类型。

yc_8301 2009-03-12
  • 打赏
  • 举报
回复
坐板凳学习!
up!
mdejtod 2009-03-12
  • 打赏
  • 举报
回复
谢谢楼上热心的兄弟。现在 好像可以了。

而对于引用类型(如string、对象实例),只要保存引用的地址就可以了。如:
var
obj: TObject;
p: Pointer;
begin
//...
p := obj;
end;
这种方法是不是不用自己去分配和管理内存?

还有就是
var FarrayVariant : array of pointer;
For i := 0 to 1o do
begin
DriveString := '测试' + inttostr(i);
SetLength(FarrayVariant,Length(FarrayVariant) +1 );
FarrayVariant[Length(FarrayVariant) -1 ] := strNew(PChar(DriveString)) ;
end;
For i := 0 to 1o do
begin
StrDispose(Pcahr(FarrayVariant[i]));
end;
这个释放的操作,怎么内存好像没有任何变化?就是不管执不执行这个操作,内存没有变化

那最后,我是不是可以采用这种方式来保存字符串和类对象?

而对于引用类型(如string、对象实例),只要保存引用的地址就可以了。如:
var
obj: TObject;
p: Pointer;
begin
//...
p := obj;
end;

-----------
非常感谢!!
ZuoBaoquan 2009-03-12
  • 打赏
  • 举报
回复
我试着总结一下,如果要实现你的目标(数组元素可以是整型、字符串或对象实例),需要考虑下面几个问题:

1. 如何将某个变量的值保存到数组内?
对于值变量(如Integer、Int64,作为局部变量、参数时分配在栈上),最稳妥的方法是重新分配一块内存保存这个值。如:
var
p: PInteger;
n: Integer;
begin
n := 2;
New(p); // 使用New手工分配内存时,所传递的指针参数必须是有类型的,这样编译器才知道要给变量分配多大的内存
p^ := n; // 将变量n的值拷贝到指针p所指向的内存的值
Dispose(p); // 手工释放内存
end;

而对于引用类型(如string、对象实例),只要保存引用的地址就可以了。如:
var
obj: TObject;
p: Pointer;
begin
//...
p := obj;
end;

这里顺便提一下,Delphi里面有些类型(如string、array)是由编译器自动维护引用计数的,如果使用无类型的Pointer则需要注意这一点(可能会引用无效的值)。

2. 如何知道某个数组元素的类型?
很显然,如果数组定义成array of Pointer,我们是没办法知道元素所代表的实际类型的。

明白了上面两点,问题就简单多了。

我们先看看最简单的方法:
type
TDataArray = array of Variant;

这种方法可以保存Delphi内大多数的简单类型的值,而且无需管理内存,但美中不足的是Variant不能直接保存对象实例的地址。当然,这个时候可以按照楼上的变通一下,采用某种类型来保存对象实例的地址,使用时再强制转换即可。

另有一种方法就是使用TVarRec:
type
TDataArray = array of TVarRec;
这种方式支持Delphi的部分复杂类型(如Class),但需要手工分配及释放内存,而且操作上比较麻烦,需先根据VType判断类型,且不能想Variant那样直接赋值。

还有一种方法就是自己封装一个结构。当然,如果可行的话,我建议使用对象来封装。如:

TDataObject = class
protected
function GetAsString: string; virtual; abstract;
public
property AsString: string read GetAsString;
//...
end;

TInteger = class(TDataObject)
//...
end;

TDataArray = array of TDataObject; // 如果使用TObjectList更方便。
金卯刀 2009-03-12
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 mdejtod 的回复:]
还有就是
type
TarrayPointer = array of Pointer;
var Flist : TarrayPointer ;

这种可否采用这种方法,将类对象存入到数组中?
就是先申请内存,将对象拷入内存块,然后再赋给数组,能否行得通不?
[/Quote]
用Pointer也可以,但不必拷貝對象,只需記錄對象地址即可。需要知道對象類型,可以聲明一個record,元素包括 類型,值或者地址

用variant也是可以的,因為記錄的只是指針而已。
mdejtod 2009-03-12
  • 打赏
  • 举报
回复
还有就是
type
TarrayPointer = array of Pointer;
var Flist : TarrayPointer ;

这种可否采用这种方法,将类对象存入到数组中?
就是先申请内存,将对象拷入内存块,然后再赋给数组,能否行得通不?
mdejtod 2009-03-12
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 ZuoBaoquan 的回复:]
StrNew和StrDispose用法很简单:
var
p: PChar;
begin
p := StrNew(PChar('hello'));
StrDispose(p);
end;
[/Quote]

这是我的释放的代码,运行到某些元素下标时会报错,无效指针、、不知为何
for i := low(FList) to High(FList) do
begin
StrDispose(pchar(Flist[i]));
end;



[Quote=引用 10 楼 Avan_Lau 的回复:]
多類型的話,可以用 array of variant;
對于基礎類型,可以通過varType取得。若是復雜類型(如class),無法直接獲得,若是對象,可通過所記錄的地址(類型為varLongWord),訪問負偏移地址vmtTypeInfo可以取得類型信息,如PPointer(Integer(v[i]) + vmtTypeInfo)^。
[/Quote]

起先也是考虑用变体类型,可是里面要存放类对象。。。。

[Quote=引用 8 楼 sanguomi 的回复:]
引用 6 楼 mdejtod 的回复:
需求是这样的,我在数组里存放一组元素,有可能是字符串,有可能是整形,也有可能是类对象,这样的话,要以什么方式存入?


你用个LIST 去添加试下
[/Quote]

我想先试一下指针数组的方法可行不
金卯刀 2009-03-12
  • 打赏
  • 举报
回复
多類型的話,可以用 array of variant;
對于基礎類型,可以通過varType取得。若是復雜類型(如class),無法直接獲得,若是對象,可通過所記錄的地址(類型為varLongWord),訪問負偏移地址vmtTypeInfo可以取得類型信息,如PPointer(Integer(v[i]) + vmtTypeInfo)^。
ZuoBaoquan 2009-03-12
  • 打赏
  • 举报
回复
StrNew和StrDispose用法很简单:
var
p: PChar;
begin
p := StrNew(PChar('hello'));
StrDispose(p);
end;
sanguomi 2009-03-12
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 mdejtod 的回复:]
需求是这样的,我在数组里存放一组元素,有可能是字符串,有可能是整形,也有可能是类对象,这样的话,要以什么方式存入?
[/Quote]

你用个LIST 去添加试下
ahjoe 2009-03-12
  • 打赏
  • 举报
回复
申请一个缓冲区,把串放进去。
mdejtod 2009-03-11
  • 打赏
  • 举报
回复
type
TarrayVariant = array of PChar;
FarrayVariant :TarrayVariant ;

For i := 0 to 1o do
begin
DriveString := '测试' + inttostr(i);
SetLength(FarrayVariant,Length(FarrayVariant) +1 );
FarrayVariant[Length(FarrayVariant) -1 ] := strNew(PChar(DriveString)) ;
end;

FarrayVariant 为全局变量,在其它代码段中调用时,这样是可以正常显示,但是要怎么释放这些内存?StrDispose 不知道怎么用

另外一个就是刚刚说的,这种要以什么类型的形式存入数组中?
谢谢!!

mdejtod 2009-03-11
  • 打赏
  • 举报
回复
需求是这样的,我在数组里存放一组元素,有可能是字符串,有可能是整形,也有可能是类对象,这样的话,要以什么方式存入?
Seamour 2009-03-11
  • 打赏
  • 举报
回复
Orz
当使用显示转换将string类型转成其它指针类型的时候,并不会增加它的引用计数,生存期结束后该释放就释放了
如果你不知道PChar和StrNew/StrDispose的话,那还是用PString和NewStr/DisposeStr吧……
mdejtod 2009-03-11
  • 打赏
  • 举报
回复
其实我用TObject也可以,只是知道要怎么搞,就像listbox 有这样的功能
items.addobject();这个方法,任何类型的都可以加进去,到底是什么原理?
还有就是
setlength(Fdata,length(FData) + 1);
FData[length(FData) - 1] := pointer(i);
这样又为什么可以呢?

解决方法个人推荐使用PChar来存取,当然用PString来弄也可以

------------------------------------------------------

放到数组里?FData[length(FData) - 1] := pchar(str);
一样的....
mdejtod 2009-03-11
  • 打赏
  • 举报
回复
那么我要怎么做?我在其它地方要使用的类型必须是指针类型的
Seamour 2009-03-11
  • 打赏
  • 举报
回复
string变量的实际内容在heap中分配,stack里只有个指针,delphi使用引用计数来维护string的生存期。当使用显示转换将string类型转成其它指针类型的时候,并不会增加它的引用计数,生存期结束后该释放就释放了。在其它地方再访问的时候如果只是乱码就算运气不错了,直接出内存访问错误都很正常。解决方法个人推荐使用PChar来存取,当然用PString来弄也可以

你的FData[n]是Pointer类型,New的原形是System._New:
function _New(size: Longint; typeInfo: Pointer): Pointer;
当你调用New(PTypeVar)的时候,会隐含传入SizeOf(PTypeVar^)、TypeInfo(PTypeVar^)两个参数,其中后一个是供Initialize用的。你认为SizeOf(Pointer^)应该是多少,编译器能猜出来么?
ZuoBaoquan 2009-03-11
  • 打赏
  • 举报
回复
这句代码很奇怪:
FData[length(FData) - 1] := pointer(str);

Pointer是无类型的,对于Delphi自动管理的类型(如string,array,record),强制使用Pointer转换并不会增加变量的引用计数,等到变量被回收了,再使用指针来访问就可能会得到垃圾数据。

16,748

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 语言基础/算法/系统设计
社区管理员
  • 语言基础/算法/系统设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧