请战友们帮忙解释下 对未赋值的指针调用 为什么会报异常, ( 积分: 50 )

  • 主题发起人 主题发起人 redcoffee
  • 开始时间 开始时间
R

redcoffee

Unregistered / Unconfirmed
GUEST, unregistred user!
被这个问题弄得很头疼,感觉应该是一个不难的道理,可是还是没有搞清。代码如下:
m = record
a: string;
b: string;
end;
定义一个变量为指向此结构的指针:pm : ^m;
然后直接调用 pm.a;
会报内存访问异常,为什么呢,pm虽未进行赋值,但他应该也有一个随机数在里面。
这个随机数地址处的内存单元应该有吧,即使位置不对,也应该有内存吧。
取出string长度的块,为什么会报异常。
请高人们帮忙下。
 
应该问编译器
这应该是编译器的限制,内存保护机制
 
编译器是如何判断呢,是不是会根据这个进程所能访问的逻辑地址空间还判断当前访问的内存是不是有效,还是通过判断所要读取的内容与实际内存块中查找类型是否匹配。。。
越想起乱了,睡觉,加了两天班,明天还要上班。谢谢大家,帮忙。
 
........................
看《delphi源代码分析》
 
1.那个随机地址很可能已经超出了你程序的地址空间,操作系统的内存保护机制是不允许这种操作的。
2.尽管所指向的地址是你程序的地址空间里,但是所执行的地址的数据结构跟 m 数据结构不匹配,这当然也会报错。
 
pm只是被定义了一个指针类型,它存储了一个初始化的指针,这个指针并没有指向一个m,因此访问会发生异常AV。如果定义一个m1:m,然后再将pm赋值pm:=@m1,这个时候pm才指向一个m,m1虽然没有赋值,但有初始值在里面,再访问pm.a就能读到a的初始值。
var
m1:m;
pm:^m;
s: string;
begin
pm:=@m1;
s:=pm.a; //s=''
end;
 
to liyinwei,
解释1是对的,但解释2没有道理。内存是线性空间,换句话说存储器只知道0101,不知道数据结构。
 
改成
m = record
a: ShortString;
b: ShortString;
end;
应该就可以读了,当然这种用法是绝对错误的

使用string不能读取是因为string是Delphi自己定义的长字符串类型
它和动态数组,类的对象等都是引用用法,在栈中保存指针,在堆中保存实际的内容
string的堆里的结构比较复杂
它使用实际字符串长度保存字符串内容而在Str[0]前面还有8个字节分别保存了引用次数和实际长度
这是在效率和内存占用率之间达到平衡而采取的做法
当你读取的时候,比如
a, b: string;
a := 'asd';
b := a;
那么a, b的指针指向同一堆中地址
只是在b := a;的时候引用计数+1
写入的时候当然也要对内存进行操作

一句话,无论读还是写使用string都要对内存进行写操作
所以你直接调用 pm.a的时候就会对你没有权限的内存进行写操作,这样就会报内存写入错误

Delphi的联机帮助上说明了三种发生这个异常的原因
Dereferences a nil (Delphi) or NULL (C++) pointer.
Writes to memory reserved for executable code.
Attempts to access a memory address for which there is no virtual memory allocated to the application.

改成ShortString,因为ShortString和Integer类型一样,只在栈中分配内存
所以你就可以直接调用而不出错
但还是要再说一句,没有给指针赋值就使用是绝对错误的!!

如果我说的不对请指正
 
楼上的:
liyinwei解释是对的。
在进行pm.a这一步操作的时候,系统进行了转换,将该段地址的内容转换为你定义的格式,结果发现转换不了,于是出错。
 
楼上的,请看下面的代码,你怎么解释它能执行且没有异常呢?
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
m = record
a: ShortString;
b: ShortString;
end;
pm = ^m;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
TmpPm: pm;
begin
ShowMessage(TmpPm.a);
end;

end.
 
网刚通,看到这多,先谢谢大家了,然后我再仔细一个个的看.等不急了
 
对结构中为integer这类基础数据的访问,肯定是可以的.
现在的问题就是为什么对没有赋值的指针访问会报异常,将上面各string改为integer就会得到一个原始内存值,但是也未对此类型指针赋值,不报错.
是根据类型还是根据可访问内存段来触发的.
 
白解释半天了
 
确实没看明白 :(
 
我已经说了对String的访问会执行写操作,所以会出错
 
真诚的谢谢你,不知调用未实例化对象的操作是不是也是这个道理.
 
其实不仅是写事件,就是读事件也会引起一个内存访问异常,继而由cpu传给你的程序一个消息...出错了!
 
指针要先初始化
pm : ^m;
new(pm);
......
dispose(pm);
 
to: cjwdone
应该是这样,现在问题是想知道为什么会报异常,编辑器依据什么原理触发这个异常。
即是为什么 从一个随机数地址处 向后后读N个字节后报错,当然这个随数是因为指针未被初始化造成的。muhx说得还是有些透彻,
我还没有理解问题的根本原理,,
 
应该是这样,现在问题是想知道为什么会报异常,编辑器依据什么原理触发这个异常。
即是为什么 从一个随机数地址处 向后后读N个字节后报错,当然这个随数是因为指针未被初始化造成的。muhx说得感觉透彻,之所以感觉因为
我还没是没理解问题的根本原理,那对类的某此方法呢,
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
694
import
I
D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
后退
顶部