可能是动态数组的问题。(150分)

  • 主题发起人 主题发起人 Jebit
  • 开始时间 开始时间
J

Jebit

Unregistered / Unconfirmed
GUEST, unregistred user!
这一段的代码是会出错的。
procedure TForm1.Button1Click(Sender: TObject);
var existedFile:string;
buffer:array of char;
reg:TRegistry;
newfile:TFileStream;
begin
if edit1.Text[length(edit1.text)]='/'
then
existedFile:=edit1.text+'New Oriental Words.cfg'
else
existedFile:=edit1.text+'/New Oriental Words.cfg';

newfile:=TFileStream.Create(existedFile,fmOpenRead);

reg:=TRegistry.Create;
reg.RootKey:=HKEY_CURRENT_USER;
try
if reg.OpenKey('Software/J-Studio',true)
then
begin
setlength(buffer,newfile.size);
newfile.ReadBuffer(buffer,newfile.size);
reg.WriteBinaryData('test',buffer,newfile.size);
end;
finally
reg.free;
newfile.free;
end;
end;


当我把上边的代码改成下边的,就不会出错了。
procedure TForm1.Button1Click(Sender: TObject);
var existedFile:string;
buffer:array[00..256] of char;
reg:TRegistry;
newfile:TFileStream;
begin
if edit1.Text[length(edit1.text)]='/'
then
existedFile:=edit1.text+'New Oriental Words.cfg'
else
existedFile:=edit1.text+'/New Oriental Words.cfg';

newfile:=TFileStream.Create(existedFile,fmOpenRead);

reg:=TRegistry.Create;
reg.RootKey:=HKEY_CURRENT_USER;
try
if reg.OpenKey('Software/J-Studio',true)
then
begin
newfile.ReadBuffer(buffer,newfile.size);
reg.WriteBinaryData('test',buffer,newfile.size);
end;
finally
reg.free;
newfile.free;
end;
end;
我不知道为什么用动态数组会有错。

后来我又写了一段类似的代码。更奇怪了。如下
procedure TForm1.Button1Click(Sender: TObject);
begin
self.writefile2reg('d:/1.txt','software/J-Studio','test');
end;

procedure TForm1.writefile2reg(filename:string;keyname:string;value:string);
var tempfile:TFileStream;
buffer:pchar;
reg:TRegistry;
begin
tempfile:=TFileStream.create(filename,fmOpenRead);
tempfile.Seek(0,soFromBeginning );
getmem(buffer,tempfile.size);
tempfile.ReadBuffer(buffer,tempfile.size);
reg:=TRegistry.Create;
reg.RootKey:=HKEY_CURRENT_USER;
reg.OpenKey(keyname,true);
reg.WriteBinaryData(value,buffer,tempfile.size);
reg.Free;
// freemem(buffer)
//当加上freemem的时候会出错,当不加的时候就不会出错了。
tempfile.free;
end;
在这段程序里边,把buffer改成动态数组还是会出错,用静态的依然不会出错。
太奇怪了。
 
一样的问题?
第一段:newfile.ReadBuffer(buffer,newfile.size);
reg.WriteBinaryData('test',buffer,newfile.size);
buffer 都改成buffer[0]
Delphi对动态数组和静态数组的应用是不同的
 
下面也一样
pchar相当与动态数组,但应用时是不是pchar[0]我忘了,我一般不用pchar,也尽量避免动态数组
 
多谢Avenir,我有点儿思路了。

关于第一段的代码。
我调试了一下,
procedure TForm1.Button1Click(Sender: TObject);
var existedFile:string;
buffer:array of char;
reg:TRegistry;
newfile:TFileStream;
begin
if edit1.Text[length(edit1.text)]='/'
then
existedFile:=edit1.text+'New Oriental Words.cfg'
else
existedFile:=edit1.text+'/New Oriental Words.cfg';

newfile:=TFileStream.Create(existedFile,fmOpenRead);

reg:=TRegistry.Create;
reg.RootKey:=HKEY_CURRENT_USER;
try
if reg.OpenKey('Software/J-Studio',true)
then
begin
setlength(buffer,newfile.size);//setlength 后buffer的地址是@buffer $12F56C,@buffer[0]为 $CB3960
//而buffer的内容是为$CB3960,
newfile.ReadBuffer(buffer,newfile.size);//read后@buffer[0]为 $FFFFFFFE ,@buffer不变,还为$12F56C
//我看了我用的文件,刚好开头的数据为FEFFFFFF
//buffer的内容也变为了$FFFFFFFE
reg.WriteBinaryData('test',buffer,newfile.size);
end;
finally
reg.free;
newfile.free
//出错。
end;
end;

我本认为原因是这样的。
动态数组和静态数组不一样,当调用setlength后,buffer是指向一段的内存空间,所以buffer和buffer[0]的内容与地址都是不一样的(这点应该是对的)。当调用newfile.readbuffer时,readbuffer开始向地址为@buffer的内存写入(我想应该也对)。于是就把newfile的内容给覆盖了。于是调用newfile.free就会出错。(这个应该错了)

后来我又仔细看了看,我发现newfile和reg内存所存的指针都没有变化,也就是说它们被没有被覆盖。也就是说reg.free和newfile.free并没有错。程序提示出错应该是隐含自动释放动态数组出错(出错信息为
Project launcher.exe raised exception class EAccessViolation with message 'Accessviolation at address 00404B6C in module 'launcher.exe'. Read of address FFFFFFF6'. Process stopped. Use Step or Run to continue.) 出错信息里的read of address FFFFFFF6应该就是因为我把buffer的内容变为了$FFFFFFFE。

我上边的解释所存在的问题如下:
1.如果newfile.readbuffer往地址为@buffer的内存单元写东西,它一定会把reg和newfile内存里的指针覆盖的,reg.free,newfile.free应该都会出错,但是实际却没有,难道程序会自动保护这些单元,使它们不可写吗?如果是这样的话,当执行readbuffer时就应该会出错了。
可以并没有报错,文件写进注册表也没有一点儿问题,都正确。
 

finally
reg.free;
newfile.free
//出错。
ShowMessage('')

end;
你就知道在Free时并没有出错,在ShowMessage后才出错,自动释放内存时出的错

Delphi分配变量地址大小按顺序时递减的(我也是刚知道),在执行是
@ExistFile>@Buffer>@Reg>@newfile,所以网@Buffer读入内容,不会影响Reg和newFile,所以可以Free等等,但影响到了ExistFile,在读后引用一下就知道了
如果你把变量顺序改变一下,Buffer放在reg,newfile后,会有更严重的后果
在Delphi里有许多声明为var Buffer这样的变参,在用动态数组做参数时,一定要用数组的第一个元素做参数,不能直接用数组名
其实就是的Borland自己有时也不注意这个问题,
在Delphi的帮助中,FileRead函数的Sample里有一句
iBytesRead := FileRead(iFileHandle, Buffer, iFileLength)
这里也要用Buffer[0],否则程序出错,读不到想要的内容
 
thank u
已经改正完毕。
procedure TForm1.Button1Click(Sender: TObject);
var existedFile:string;
buffer:array of char;
reg:TRegistry;
newfile:TFileStream;
begin
if edit1.Text[length(edit1.text)]='/'
then
existedFile:=edit1.text+'New Oriental Words.cfg'
else
existedFile:=edit1.text+'/New Oriental Words.cfg';

newfile:=TFileStream.Create(existedFile,fmOpenRead);

reg:=TRegistry.Create;
reg.RootKey:=HKEY_CURRENT_USER;
try
if reg.OpenKey('Software/J-Studio',true)
then
begin
setlength(buffer,newfile.size);
newfile.ReadBuffer(buffer[0],newfile.size)
// 改这里。
reg.WriteBinaryData('test',buffer,newfile.size);
end;
finally
reg.free;
newfile.free;
end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
self.writefile2reg('d:/1.txt','software/J-Studio','test');
end;

procedure TForm1.writefile2reg(filename:string;keyname:string;value:string);
var tempfile:TFileStream;
buffer:pchar;
reg:TRegistry;
begin
tempfile:=TFileStream.create(filename,fmOpenRead);
tempfile.Seek(0,soFromBeginning );
getmem(buffer,tempfile.size);
tempfile.ReadBuffer(buffer^,tempfile.size);//这里的问题,和动态数组类似
reg:=TRegistry.Create;
reg.RootKey:=HKEY_CURRENT_USER;
reg.OpenKey(keyname,true);
reg.WriteBinaryData(value,buffer,tempfile.size);
reg.Free;
freemem(buffer)

tempfile.free;
end;
 
接受答案了.
 
后退
顶部