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

  • 主题发起人 Archerfl
  • 开始时间
A

Archerfl

Unregistered / Unconfirmed
GUEST, unregistred user!
这是我写的一段代码:
procedure MyMove(const Source;var Dest;ByteCounts: Integer);
var
S,D: array of Byte;
i: Integer;
begin
if (@Source = @Dest) or (ByteCounts <= 0) then Exit;
S := @Source;
D := @Dest;
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;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
m,n: Integer;
begin
m := 10;
MyMove(m,n,4);
ShowMessage(IntToStr(n));
end;
程序很简单,只是想对传入MyMove过程的任何变量从源到目的按字节拷贝其内容。
但是,在MyMove过程的局部变量声明中,假如我如上面这种方式用动态数组array of Integer,
那么编译后可以通过,但一运行后,会报警说:“Project Project1.exe raised exception
class EInvalidPointer with message 'Invalid pointer operation'.”。而我把声明形式
改为var S,D: PChar后一切 OK!
不知道这是为什么,动态数组实质只是一个指针,那么我通过强制转换把源和目的的地址赋
给它后就可以利用其数组特性,数组名[下标]的方式进行对数组元素的操作,但为什么会报警呢?
望各位高手指点!
 
你把S,D: array of Byte;定义为全局变量试试
 
好像不行,改为全局变量后随然也可以编译通过,但运行后又有了新的报警提示,说:
“Project Project1.exe raised exception class EAccessViolation with message
'Access violation at address 004240 in module 'Project1.exe'.”。莫名
其妙,不知道为什么?
 
使用Move函数直接抄写
 
谢谢提示,Move的OP及汇编源码我都看过,不过我不是在考虑如何实现Move过程,而是在考
虑OP的指针操作,正如我上面写到的,因为OP对指针操作很严格,假如我们使用指针名[下标]
这种方式操作其中指针所指向内存区域的数据可不是随便就行的!这个指针除非是PChar或者是动态
数组什么的才行,而且由于动态数组实质就是分配了四个字节的指针,所以这种转换在Delphi
中是允许的,但不知道为什么能够编译通过,但运行后就出错呢?而且在这段代码中我想我的
编写逻辑并没有什么漏洞呀?真是搞不懂!
 
var
S,D: array of Byte;

定义错误 array of byte 是指针的指针
应做;
Type
PByteArray=^TByteArray;
TByteArray=Array [0..0] of Byte;

S,D:pByteArray



 
to GGCAT:
定义错误 array of byte 是指针的指针,这句话为什么呢?在OP中这不是动态数组
定义吗?而且难道它指向的不是实际存放数据的内存地址吗?然后源码应该如何改写,望
具体指点一下!谢!
 
我刚调试了一下这段程序,发现S的下标好像是无限大的。。。
而且,我的没有任何问题,除了在最后一句
showmessage(inttostr(n))这说invalid pointer operation
将这一句注释了以后,原来程序里总是INTEGET(S)>INTEGER(D)的,现在反过来了...
 
array of integer时
s和s[i+1]间相差4字节,而不是byte时的一字节
所以你用for i := .... to .... 操作时最后后面的操作越界了
 
你不能一步一步执行,看看错误到底出现在哪行吗?
 
to Pearl:
我好像没有明白您的意思!我在MyMove过程中声明局部变量时用的是array of Byte
,那么出于指针角度考虑问题,这时我每一次指针操作的步进长度应该为一个字节,而假如
是array of Integer的话则为4,但现在我让S,D分别指向Integer的内存首地址,然后一个
字节一个字节的操作,这难道有什么问题吗?
 
to sowen:
是的,在发出贴子之前,我用Debugger单步调试过,不过问题出在MyMove执行了最后
一条赋值语句:D := S;之后即将退出该过程时报错!但我不清楚是我哪里错了!
 
DELPHI在处理动态数组的时候和处理静态数组的方式是不一样的!!!他们在内存中不只是
简单的顺序排列,例如静态数组就有前缀字节,用于保存数组的长度. 动态的也一样,也有
这种情况.在你的问题中只要设 S,D:pchar 就可以了.
 
to 990:
您说的“关于在Delphi中动态数组和静态数组的存储方式不同,在内存中不只是简单的
顺序排列”,我写在两段程序并调试了一下,发现在Delphi中并不是这样,无论是静态数组还
是动态数组所指向的数据在内存中都是呈线性排列的!
测试代码如下:
var
a: array[1..5] of Byte;
b: array of Byte;

procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
b := @a;
for i := 1 to 5 do
a := i;
ShowMessage(IntToStr(a[1]));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
i: Integer;
begin
SetLength(b,5);
for i := 0 to 4 do
b := i;
ShowMessage(IntToStr(b[0]));
end;
 
动台数组实际上是一个指针数组,他的每一个元素是一个指向定义类型的指针,所以
用MOVE的时候会发生错误。
 
to tseug:
您说“动态数组实际上是一个指针数组,它的每一个元素是一个指向定义类型的指针。”
那么这段代码:
   procedure TForm1.Button1Click(Sender: TObject);
   var
     a: array of Byte;
     i: Integer;
   begin
     SetLength(a,5);
     for i := 0 to 4 do
      a := i;
     ShowMessage(IntToStr(a));
   end;
中a[下标]用调试器调试后发现确实是实际的数值,正如上面赋值语句所赋的一样。并不是
32位指针呀!
 
if Integer(D) > Integer(S) then
这行是什么意思?

让人困惑的是
你的程序其实都完成了操作,在IntToStr出错……
你试试看,把ShowMessage注释掉,就没问题了。
debugger查看,程序正确。
但加上inttostr就会出错
甚至IntToStr(m)都出错
我怀疑你的指针把IntToStr函数破坏了,所以才会无效指针操作。

不过还有一件更奇怪的事情,添加一个过程:
procedure a(b:integer);
begin
ShowMessage(IntToStr(b));
end;
把ShowMessage(IntToStr(n));改a(n);
然后竟然可以了……不要问我为什么,我也不知道……建议查查IntToStr的代码,我最近没空……
大家的讨论其实都有点离题了……这样看来MyMove应该是没有什么问题的……Archerfl老兄的本来目的应该已经达到了……
 
仁兄还真是认真啊,呵呵!没错,都是成线性排列的. 但在内存中除了保存数组数据以外,还有当前数组
的状态!!! 你只模拟了数据,但没有模拟数组状态数据,所以一执行就会出错. 因为在本应该是数组状态
的内存位置存放着未知的数据.你编译的时候是可以通过的,因为没有语法错误.

还有就是你研究这个问题没有多大的意义啊,你又不写ASM.反正只要一切按照规矩写代码.
(你要是还不明白我就没有办法了,呵呵)
 
呵呵,990仁兄说得有道理……但还有几个问题,为什么按我那样加一个过程就可以了?怎样复制数组状态数据?还有,数据返回以后已经是integer了,数组状态信息还有没有应该已经无所谓啦……
 
to hbbs:
if Integer(D) > Integer(S) then这句的意思是判断D所指向的内存地址值是否高于
S所指向的内存地址值。不错,如果屏蔽掉随后的ShowMessage()程序,已经可以正常运行。
但加上后就报错。但假如改为ShowMessage('Hello');然后在这句加断点,这时你会发现m,
n值已经相等了均为10。
to 990:
其实,我考虑这个问题的原因上面我已经说了,假如这样做允许的话,那我们今后就
可以用数组名[下标]这种方式访问数据啦,需要的无非是声明一个动态数组指针,然后让
它指向要访问的内存区域即可!但是... ...
  而且,实际当中这样写还会问题:
  procedure TForm1.Button4Click(Sender: TObject);
var
i: Integer;
k: array of Byte;
begin
k := @i;
k[0] := 100;
ShowMessage(IntToStr(i));
end;
按理说,我对i的内存赋值了,应该ok的,但是又是编译通过但运行后报警!说什么访问
地址违例之云!
另外,您提到“动态数组在内存中除了保存数组数据以外,还有当前数组的状态”,那么您
说的这个状态值在内存中什么位置呢?能否给出解释,或者是您的测试代码?
 

Similar threads

S
回复
0
查看
950
SUNSTONE的Delphi笔记
S
S
回复
0
查看
771
SUNSTONE的Delphi笔记
S
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
736
import
I
顶部