PDA

View Full Version : Strange AV?



chronozphere
11-11-2010, 06:41 PM
Hey everyone,

I'm getting a strange AV. Don't really know why it's popping up.



type
PMyRec = ^TMyRec;
TMyRec = record
Str: String;
Int: Integer;
end;

procedure TForm1.Button5Click(Sender: TObject);
var
P: PMyRec;
begin
GetMem(P, sizeof(TMyRec));
P^.Str := 'test'; //<< AV HERE!
P^.Int := 5;

FreeMem(P);
end;


This should go fine. I just set the Str which is actually a special kind of pointer, to point to 'test'.

Can somebody enlighten me?

Thanks :)

Andru
11-11-2010, 06:58 PM
Hmm, strange, because only one line of code is wrong:


FreeMem( P );


You must free String in record first, like this:


P^.Str := '';

And only then call FreeMem.

But... try to call FillChar( P^, SizeOf( P^ ), 0 ) after GetMem

chronozphere
11-11-2010, 07:06 PM
Hmm.. this is really odd. It does work if I replace getMem() and FreeMem() by New() and Dispose(). ???

Does anyone know the differences between these functions?

I'll do some more testing ASAP. Thanks for your reply. ;)

Edit:



GetMem(P, sizeof(TMyRec));
FillChar( P^, SizeOf( P^ ), 0 );
P^.Str := 'test';
P^.Int := 5;


This works too. How is that possible?

JSoftware
11-11-2010, 07:20 PM
GetMem(P, sizeof(TMyRec));

results in P, a chunk of allocated memory filled with crap. When you assign a string to this pool of crap you first have to free the old string. If it tries to free Nil then it won't try, but if there's any value but nil, then you are freeing something that isn't allocated

Basically, you need to use either allocmem(it'll zero out the memory), or use fillchar after getmem. They are basically the same

Edit: and I think new uses the same method as allocmem. Eg. zeroes before returning

de_jean_7777
11-11-2010, 07:29 PM
new and dispose are used with pointers to records or other structures that contain automated types, so they are initialized/finalized properly. Since the string in your example is probably an ansistring, and therefore an automated type, you need to use new and dispose, which I suggest you use instead of getmem/freemem. I only use getmem/freemem for memory which has no type (e.g. plain pointers). As JSoftware said, the compiler will try to free the old string when it assigns the new one, but there is no old string, only garbage which was on the heap where your record got allocated.

User137
12-11-2010, 01:23 AM
Str: String;
is merely a pointer.

You should define strings with length if you want to use them in data blocks:


Str: String[64];
I think this is will take 65 bytes but usable like normal string. Just that it can have at max 64 characters if defined like that. You can alternatively use array[1..64] of char;

chronozphere
12-11-2010, 07:59 AM
That might be an appropriate sollution for me. :)

String[64] is basicly a shortstring with a maximum capacity of 64 chars, right?

User137
12-11-2010, 10:16 AM
I'm not exactly sure... You might be able to define it like String[4600] but i haven't tested that, not home atm either. At least String[255] should work no problem but you want to keep it as small as possible depending on average string length needed in it. It does reserve the whole space in memory.

Jimmy Valavanis
12-11-2010, 11:23 AM
Short strings can have 255 characters maximum, so we can not define string[4600] (at least with older Delphi compilers, I'm not familiar with the latest versions). Calling FillChar(P^, sizeof(TMyRec), 0); after GetMem() will eliminate the problem even without the restriction of the short string.

User137
12-11-2010, 02:40 PM
I just tried your code with Lazarus and it doesn't give AV in objfpc or delphi modes. Works normally. It should be close to same thing as if you define a string variable in a procedure; initial value isn't initialized and when new value is stored, the trashcollector would try to free the old string which doesn't exist and give AV. However procedures do initialize string variables! If you make:
edit: JSoftware already said the same...

var s: string;
begin
memo1.Lines.Add(s);
It will print just a line chance, it never adds random characters. This however isn't case if you directly assign a memory space for it. Fillchar fixes that.

string[4600] didn't even compile, max was indeed 255. But this works:

s: array[1..6400] of char;
...
procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
//s:='test'; // This works too with proper cut after 4 characters
for i:=1 to 6400 do
s[i]:=inttostr(i mod 10)[1];
// [1] picks first and only character of inttostr() which results as string, expected char
memo1.Lines.Add(s); // 6400 characters of continuous 0-9
end;