关于String的问题 ( 积分: 50 )

  • 主题发起人 主题发起人 Captain Ho
  • 开始时间 开始时间
C

Captain Ho

Unregistered / Unconfirmed
GUEST, unregistred user!
type
TMy = record
a : Integer;
b : Single;
c : String;
end;
PMyArray = ^TMyArray;
TMyArray = array[0..1] of TMy;
……
procedure TForm1.Button1Click(Sender: TObject);
var
i : Integer;
my : PMyArray;
begin
GetMem(my, SizeOf(TMy)*10);
for i := 0 to 9 do
begin
my.a := i;
my.b := i + 0.1;
my.c := '123';
end;
FreeMem(my);
end;

上面的代码,我已经确认,在D2007up1-up3里面只能正确编译,而运行时就会报错。
但是在D6里,编译和运行都没有任何错误。

问题我知道如何解决,只是想知道从哪个版本开始出现的问题。
所以请说出你们使用的Delphi版本,和是否出现错误就行。
合格者,每人5分。
 
D6 行吗? D7 开始就不行了。
 
内存越界sizeof(TMy)<sizeof(TMyArray)
 
D6完全没有任何问题,编译和运行都没有错误。
siking,你的回答没有按照要求,不能给分了。
 
在GetMem之后加上fillchar(my^,SizeOf(TMy)*10,0)
 
&quot;D6完全没有任何问题,编译和运行都没有错误。&quot;

我觉得D6中也是有问题的
Integer和Single都是简单类型,内存在栈(Stack)中分配
而String不是简单类型,在栈中只是分配了一个四个字节的空间并且为nil
当执行my.c := '123';
的时候在堆(Heap)中分配了内存,在栈中的这四个字节保存了字符串在堆中的位置(类似$00101100之类的地址),而这个地址之后保存的是'123',之前的八个字节保存了字符串大小和引用次数。

所以FreeMem(my);
的时候释放了栈里的内存,而堆里的内存('123'.及8个字节)没有被释放,就造成了内存的泄漏
 
TO lxddd:
你的解决方法我没有试过,但是,FillChar是比较慢的操作,所以不推荐这么用。

TO muhx:
我说的没有问题,只是单指编译和运行。
至于内存是否泄漏,我没有研究过。
不过,String类型是Delphi通过引用计数来管理的,基本不需人为干预。
 
我认为是内存初始化的问题——因为你的Record内部有String这样的指针类型,你应当在
GetMem之后对这块内存进行清零(使用FillChar或者ZreoMemory),然后再正常使用。在清
零之前,String变量实际上是未初始化的“野指针”,Delphi会在赋值之前对将这个野指针
视为正常的String并进行引用计数方面的操作,从而造成错误。
btw: String的确有引用计数,但是对于在Record内部的String,Delphi不会自动释放,
原因很简单,因为Record没有析构函数,编译器不知道Record的内存什么时候被释放(标准
的FreeMem函数不会管指针的实际类型——它只是简单的将指针占用的内存标记为未使用罢
了)。所以,你除了在使用Record之前进行清零之外,还要在最后FreeMem之前显式的将每
个记录中的String置为空。
 
如果你的 my.c不是很长的话,请用string[255];否则,申明为array [0..xx] of char;

因为 string 类型是一种类似于PChar的类型,占4个字节,你在他配内存时,并没有分配空间给它,只是给它分配了一个地址
 
谁告诉你fillchar比较慢?相反它是数据块清0最快的方法.这是Delphi的fillchar标准汇编代码:
push edi
mov edi,eax
mov ch,cl
mov eax,ecx
shl eax,16
mov ax,cx
mov ecx,edx
sar ecx,2
js @@1
rep stosd
mov ecx,edx
and ecx,3
rep stobs
@@1:
pop edi
ret

总共31字节,运行时间可以忽略不计.
如果一定要在记录中使用string,而且需要GetMem记录,fillchar是最快也最省事的方法
 
呵呵,楼上的各位,都没有仔细看我的帖子吧。
我不是来寻求如何解决问题的,
我只想知道,我的代码在哪个版本的Delphi里,编译后,运行会报错。

to lxddd:
我说的FillChar比较慢,是和直接赋值的速度进行比较的。[:D]
 
1.从string的工作原理来说,你的代码在到目前为止所有Delphi版本里都会出错,除非将来某个版本的Delphi在GetMem过程里加入了清0代码.而在D6里不出错只是偶然.你可以多试几次,肯定会出错.
2.在你的代码里的my.c,没有任何办法对它直接赋值.
 
D7+up1,有错,访问my[0].c错误。

将getmem(my, sizeof(tmy) * 10)
==>
my := allocmem(sizeof(tmy) * 10);
则正常。
 
在D7下编译正常,但运行报地址错.同楼上:
my := AllocMem(SizeOf(TMy)*10);
for i := 0 to 9 do
begin
my.a := i;
my.b := i + 0.1;
my.c := '123';
end;
FreeMem(my);
allocmem功能分配内存并初始化.如果没有必要初始化就用getmem就OK
 
to lxddd:
D6里确实也会出错,但不是很明显。看下面的代码。
type
TMy = record
a : Integer;
b : Single;
c : String;
end;
PMyArray = ^TMyArray;
TMyArray = array[0..1] of TMy;

TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
i : Integer;
my : PMyArray;
begin
GetMem(my, SizeOf(TMy)*10);

for i := 0 to 9 do
begin
my.a := i;
my.b := i + 0.1;
my.c := '123';
end;

i := 0;
Self.Canvas.TextOut(0, 0, IntToStr(my.a));
Self.Canvas.TextOut(0, 20, FloatToStr(my.b));
Self.Canvas.TextOut(0, 40, my.c);
i := 9;
Self.Canvas.TextOut(0, 60, IntToStr(my.a));
Self.Canvas.TextOut(0, 80, FloatToStr(my.b));
Self.Canvas.TextOut(0, 100, my.c);
FreeMem(my);
end;
在你第一次按下按钮时,可以得到正确的结果。但再次按下按钮时,就会报错。
把String类型改成PChar类型,就没有错误了。
为什么会有不同的待遇呢?
呵呵。
 
楼主的FreeMem会有内存泄漏.
因为结构中的字符串域是生存期自管理的.GetMem和FreeMem并没有初始化和反初始化生存期自管理的类型.所以会有问题.
看看帮助中的Freemem的备注部分.
Note: It is preferable to use the New and Dispose procedures rather than GetMem and FreeMem. When using New and Dispose, there is no need to explicitly call Finalize.
要么用New和DisPose.要么在使用GetMem之后Initialize一下,在FreeMem之前Finalize一下.
 
to 楼主: 这是代码错误!
以前的帖子讨论过这个问题的: 数据结构中使用无长度的 string 肯定是错误的;
你在D6中编译运行都没有问题, 可能你选择的是没有开hugeStrings, 而使用的是shortStrings;

shortStrings固定=String[255], 是固定长度, 所以不会错;

使用HugeStrings, 结构中使用String 肯定错误!

建议: 估计结构中可能的最大字符串长度, 使用String[max_length];//在结构中使用固定长度的字符串!!!
 
呵呵,都是String惹的祸,Delphi引入String看来还是有点隐患的。
 
散分,结贴。
使用PChar就行D。[:D]
 
后退
顶部