困惑!有关动态数组的使用,怎么会这样呢?(20分)

  • 主题发起人 主题发起人 Archerfl
  • 开始时间 开始时间
应该是delphi优化程序搞得鬼。 很简单就可以证明这点, 只要你把i定义为全局变量, 那么
k[0]:=100的确可以给i赋值。 但是在退出buttonclick时会报非法内存访问错。 这就是内存收集程序搞出来的了。

因为delphi认为你在过程中为k分配了内存。 所以在过程结束时delphi准备释放k这个局部变量了。[:D]

动态数组虽说本质上是指针, 但是它的内存管理已经由delphi自动控制了, 所以最好把动态数组当成一种普通类型类来使用(其实内存管理上动态数组和string很相似, 只是没有像string那样检查严格罢了)。
 
to Pearl:
 首先,感谢您的帮助,我按照您的说法将上面那段程序调整如下:
    var
i: Integer;
procedure TForm1.Button4Click(Sender: TObject);
var
k: array of Byte;
begin
k := @i;
k[0] := 100;
ShowMessage(IntToStr(i));
end;
这次运行正确了,并没有报错!
您提到这是由于Delphi优化程序搞得鬼,这句话什么意思呢?望您指点!
其它的我表示接受,出错后我看了一下反汇编,发现确实Delphi在退出过程时试图释放被该
动态数组占据的内存,所以... ...
 
To Archerfl
您说“if Integer(D) > Integer(S) then这句的意思是判断D所指向的内存地址值是否高于S所指向的内存地址值。”这我知道啊……我是问为什么要判断这个……
Pearl说得有道理,那照那样修改,程序结束时释放i的内存的时候会不会出错?为什么照原来那样写会在IntToStr处出错,注释掉它就不出错了呢?
还有,在k[0]:=100前加SetLength(k,4);也不会出错,为什么啊?一开始我还以为是数组下标越界呢……呵呵~~
 
为什么不蛇定动态数组的区域大小,没有划分内存空间你是准备放在什么地方的?
PLEASE WRITE “SETLENGTH(?,?)”。。。。。。
 
SetLength(k,4);给k分配另外的内存了。
我说是优化程序搞得鬼是因为delphi编译器对局部变量有很多优化措施, 以确保生成最高效的代码。 比如如果定义了一个内存变量但在程序中并未调用, 则delphi优化时根本不会在堆中分配内存。 又比如在过程中间局部变量就可能已经被释放掉了(或地址空间被其他变量占据了), 而不必等到过程结束再释放。 又比如对某些局部变量程序根本不分配内存, 而是直接将它赋给了某个寄存器。
很显然你的测试程序中并没有直接给i赋过值, 所以我怀疑是优化程序搞得鬼。
 
动态数组是生存期自管理的,离开自作用域时它们会被释放。
把mymove过程的调用直接写入(而非调用),则无上述问题。
代码如下
procedure TForm1.Button1Click(Sender: TObject);
var
m,n: Integer;
S,D: array of Byte
//pchar;//
i,ByteCounts : Integer;
begin
m := 10;
ByteCounts :=4
//**
if (@m = @n) or (ByteCounts <= 0) then Exit;
S := @m;
D := @n;
if Integer(D) > Integer(S) then
for i := ByteCounts - 1 downto 0 do
D := S
else
for i := 0 to ByteCounts - 1 do
D := S
//原形是mymove()
ShowMessage(IntToStr(n));
end;
由此可以断定问题的根源在于:由于D,S离开作用域的自动释放机制,
引起了指针混乱。(赋值都不行。)
至于hhbs提出的怪现象(不过还有一件更奇怪的事情,添加一个过程:
procedure a(b:integer);
begin
ShowMessage(IntToStr(b));
end;
把ShowMessage(IntToStr(n));改a(n);
然后竟然可以了……)
不知如何解释更好。但如果是调用一个函数则会出现错误。
 
to hbbs:
    为什么要判断源和目的地址大小,是由于怕引起覆盖。你可以参考Move过程OP源码。
to delphikj
同意您的观点!在退出过程时,由于动态数组是生存期自管理类型,所以要释放内
存,但由于我们并没有用SetLength为其指定内存区域,所以它这时的释放可能会对其它全局
数据产生影响,这可能就是原因之一... ...
不过,为什么我这样写也会产生问题呢?
   procedure TForm1.Button4Click(Sender: TObject);
   var
    i: Integer;
    k: array of Byte;
   begin
    k := @i;
    k[0] := 100;
    // ShowMessage(IntToStr(i));
   end;
报错,在k[0] := 100;这句处,这时程序应该还没有释放被k所引用的内存。但是... ...
而且加不加ShowMessage这句一样!
 
乱七八糟的。
方法一:
// S,D: array of Byte;
S,D: PByteArray
//不是有人说了这样做就行了吗?

方法二:
打开project-->options-->....关掉优化选项 //不是也有人说了这样做就行了吗?
 
按delphikj的说法,那么在函数结束前把s,d指针都恢复原状,是不是就不会出错了?
 
to hbbs:
我试了一下,这样好像也不行呀!你成功了吗?
 
呵呵,我也不行
 
》》Archerfl
还是生存期的问题呀;不信你把
 var
    i: Integer;
    k: array of Byte;
的位置变为全局变量,就正常了。(但当关闭窗口时会触发异常。)
》》hhbs
"那么在函数结束前把s,d指针都恢复原状,是不是就不会出错了?"
问题是出了生存域s,d相当于执行了s:=nil;d:=nil;了,这时候指定
s,d指针都恢复原状还会有意义吗?
 

S,D: array of Byte;
改成
S, D: PByteArray;
一切OK。
@Source和@Dest都是取指针的,怎么把一个指针赋值于动态数组呢?
PByteArray才是数组指针,指针给指针,不是很好理解么?
 
fshell和GGCAT所言是正途;PByteArray应该是指向静态数组的指针。
而通过动态数组指针的方法看来是不可取的。
 
散分啦!
 
讨论到此为止,看样子使用动态数组的方法还是不可取的,谢谢大家!
 

Similar threads

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