[DUG] [computing] Sizeof record gives error
Jolyon Smith
jsmith at deltics.co.nz
Thu Sep 1 09:41:08 NZST 2011
FillChar (#0) is equivalent of calling "Initialize()", because that is what
"initialisation" means: an empty string, an empty dynamic array, an
unassigned interface/object reference or pointer... these are all NIL, which
conveniently is of course 0 (zero). But if ever the RTL implementation of
initialization for these types changed (however unlikely that might be) then
FillChar(#0) would stop working.
If you need plums, order plums. Don't order "stone fruit" just because you
happened to get plums last time. You might get apricots.
:)
On 31 August 2011 21:02, David Moorhouse <delphi at moorhouse.net.nz> wrote:
> You're both right. A call to GetMem followed by FillChar with #0 sorts
> the problem :(
>
> However I will stick with New as it is a cleaner solution than all others
> mentioned :)
>
> D
>
>
> On 30/08/11 12:51, David Brennan wrote:
>
> I think the sizeof call is irrelevant here (which I believe is what
> Jolyon is also saying).****
>
> ** **
>
> The error occurs on this line:****
>
> LogData.LogArgs := CreateConstArray(Args);****
>
> ** **
>
> LogArgs is a dynamic array as is the CreateConstArray result. Standard
> Delphi logic is that the memory for the old dynamic array (ie LogArgs) will
> be dereferenced and then freed if no longer used. If LogArgs points to
> random memory because it was never initialised then obviously the
> dereferencing and subsequent freeing are both extremely likely to blow up.
> ****
>
> ** **
>
> The compiler usually works pretty hard to make sure dynamic array variables
> are initialised to nil but the GetMem call was bypassing that.****
>
> ** **
>
> Cheers,****
>
> David. ****
>
> ** **
>
> ** **
>
> *From:* delphi-bounces at listserver.123.net.nz [
> mailto:delphi-bounces at listserver.123.net.nz<delphi-bounces at listserver.123.net.nz>]
> *On Behalf Of *Jolyon Smith
> *Sent:* Tuesday, 30 August 2011 12:04 p.m.
> *To:* rohit at cfl.co.nz; NZ Borland Developers Group - Delphi List
> *Subject:* Re: [DUG] [computing] Sizeof record gives error****
>
> ** **
>
> No, it is not. The compiler is perfectly correct.
>
> A "dynamic array" is a reference type. It is a POINTER to a structure that
> describes a dynamically sized and reference counted array, just as a
> "String" is a pointer to a structure that describes (in even more detail) a
> dynamically sized, reference counted String. In fact, an "array of Char",
> plus twiddly bits. :)****
>
> sizeof(TObject) = 4****
>
> sizeof(Pointer) = 4****
>
> sizeof(String) = 4****
>
> sizeof(dynamic array) = 4
>
> Is correct and true because all four types are _pointers_ and (on Win32) a
> pointer is 32-bits == 4 bytes.
>
> The size of/amount of memory that each variable of those types points _to_
> is different, and includes not only the "user data" (at the positive offsets
> from the address that the pointer references) but also RTL information at
> *negative* offsets. But "sizeof" is not concerned with (is in fact entirely
> ignorant of) this data, only the size of the type used to hold the
> *reference* to that data.
>
> ****
>
> type****
>
> tfoo = record****
>
> bar: array of integer;****
>
> end;****
>
> ** **
>
> var****
>
> f: TFoo; // << has a single field holding a *reference* to an empty
> array of integer;****
>
> begin****
>
> // sizeof(f) == 4 at this point - a pointer****
>
> ** **
>
> SetLength(f.bar, 20); // makes "bar" big enough for 20 integers - 80
> bytes****
>
> ** **
>
> // sizeof(f) is STILL *4* at this point because "bar" is still just a
> pointer. ****
>
> // Furthermore, sizeof() is evaluated when you *compiled* the code so it
> cannot know ****
>
> // how much memory bar points to even if it wanted to.****
>
> end;****
>
> ** **
>
> ** **
>
> It is the *initialization* of the memory *referenced* by your dynamic array
> variable that is crucial to the correct functioning of the "compiler magic"
> that supports such types. If you run this code in the debugger and place a
> breakpoint on the "SetLength", when the code stops on that breakpoint open
> the CPU view and look back and you will see that the compiler has injected a
> call to "InitializeRecord"... if you look in System.pas you will see that
> _InitializeRecord in turn calls _InitializeArray.****
>
> It is this sequence of "Initialize" calls that is missing when you simply
> allocate a chunk of memory and then set a typed pointer to point at it
> without first doing all the housekeeping necessary to make that chunk of
> memory *behave* in the way that your typed pointer is going to expect it to.
> ****
>
> ** **
>
> By all means do that simple memory allocation, but you are then responsible
> for doing the housekeeping... calling "Initialize".****
>
> ** **
>
> Or use "New" and let the RTL take care of any initialisation that may be
> required.****
>
> ** **
>
> ** **
>
> ** **
>
> On 30 August 2011 09:10, Rohit Gupta <rohit at cfl.co.nz> wrote:****
>
> Joylon,
>
> Good point. But I dont think it applies here. Sizeof returns an incorrect
> result... Its a compiler flaw.
>
> Rohit****
>
>
>
>
>
> On 29/08/2011 8:10 p.m., Jolyon Smith wrote: ****
>
> Having just looked up the references for New/GetMem it may not actually be
> a question of amount of memory allocated after all, but rather one of
> correct initialization of the allocated memory.****
>
> ** **
>
> GetMem() does not initialize the allocated memory, New() does.****
>
> ** **
>
> This is explained in the documentation for the "Initialize()" procedure (it
> might have been helpful if the documentation for GetMem() had mentioned this
> - or the lack there-of - too - LOL)****
>
> ** **
>
> So you might find that GetMem() would have worked had you also called
> Initialise() on the pointer afterward (and Finalize() before calling
> FreeMem()). But whatever the underlying reasons it is obviously simply much
> easier and safer (less to remember to have to do, less to get wrong in doing
> it :)) to use New/Dispose when working with allocations of
> variables/structured(typed) memory and use GetMem/FreeMem when it is
> strictly speaking *just* unstructured memory you need (e.g. i/o buffers
> etc), rather than space for variables.
>
> Glad it helped with your problem in any event. :)****
>
> ** **
>
> ** **
>
> On 29 August 2011 17:40, David Moorhouse <delphi at moorhouse.net.nz> wrote:*
> ***
>
> I'm aware of the compiler layout behind the scene - and the fact that
> regardless of the length of the dynamic array, my call to GetMem (or New)
> does NOT have to allocate memory for the dynamic array's contents, just it's
> overhead :)
>
> However, the compiler gets the size right using New rather than GetMem :)
> So thanks for the tip.
>
> Cheers
>
> D ****
>
>
>
>
>
>
> On 29/08/11 16:23, Jolyon Smith wrote: ****
>
> Is it a compiler *error* or just a compiler _behaviour_ ?
>
> I haven't looked into it in detail, but dynamic arrays are notoriously
> slippery when you are working with them at a low level and alarm bells
> started ringing as soon as I saw they were involved. ****
>
> ** **
>
> In particular, a dynamic array is a reference type, like a string. So
> whilst their may be additional RTTI at a negative offset from the base
> address of the array, the "array" itself may well be a pointer, hence
> "sizeof()" will return 4 - the size of a pointer - no matter how many items
> may be in the array (as opposed to Length(), obviously).
>
> NOTE: sizeof(String) also yields "4" even though we all know that a
> String variable requires many more bytes than that.****
>
> ** **
>
>
> As far as this particular example goes, do you get any better results using
> the typed New() function rather than GetMem() which knows nothing about the
> "type" of memory required by the pointer you are initialising and just
> blithely allocates the specified number of bytes...:****
>
> ** **
>
> Instead of >> LogData := GetMem( ... ); ****
>
> ** **
>
> Use >> New( LogData );
>
> And see if you get better results. :)
>
> (Also, don't forget to use "Dispose()" to deallocate the memory obtained
> with "New()", rather than FreeMem())****
>
> ** **
>
> On 28 August 2011 21:33, David Moorhouse (DUG) <delphi at moorhouse.net.nz>
> wrote:****
>
> I believe it is a compiler error and will raise a QA ticket
>
> Thanks for your help
>
>
> D****
>
>
>
> On 26/08/11 08:17, Peter Ingham wrote:
> > Try filling LogData with binary zeros after the Getmem& before the
> assign.
> >
> > FillChar (LogData^, sizeof(TLogData), 0);
> >
> > I believe the uninitialized memory is messing up the compiler magic
> > associated with the dynamic array.
> >
> >
> > Any reason the local Tlogdata record is referenced via a pointer?
> >
> > I suspect the following will also work:
> > procedure TUserClass.Log(const LogType: TLogType; const Args: array of
> > const );
> > var
> > LogData: TLogData;
> > begin
> > LogData.LogType := LogType;
> > LogData.LogArgs := CreateConstArray(Args);
> > // ... do some other stuff with the LogData item finally calling
> > end;
> >
> > Cheers
> >
> > On 26/08/2011 1:49 a.m., David Moorhouse wrote:
> >> Hi Peter
> >>
> >> Been there done that :)
> >>
> >> The function call is fine. It is the assignment that causes the AV -
> >> because the "bucket" is too small.
> >> Assigning it with 16 bytes fixes the problem, regardless of how many
> >> items the array holds.
> >>
> >> I smell compiler magic in the background.
> >>
> >> Cheers
> >
> >> D
> >>
> >> On 25/08/11 17:29, Peter Ingham wrote:
> >>> Another attempt to reply...
> >>>
> >>> First thing to do is determine if the crash occurs in the procedure
> call,
> >>> on the subsequent assign, or in between.
> >>>
> >>> Give this a try:
> >>> procedure TUserClass.Log(const LogType: TLogType; const Args: array
> of
> >>> const );
> >>> var
> >>> LogData: PLogData;
> >>> TempArgs : TConstArray;
> >>> begin
> >>> // size of record TLogData does not work
> >>> GetMem(LogData, sizeof(TLogData));
> >>> LogData.LogType := LogType;
> >>> // blows up on one of these lines
> >>> TempArgs := CreateConstArray(Args);
> >>> LogData.LogArgs := TempArgs;
> >>> // ... do some other stuff with the LogData item finally calling
> >>> FreeMem
> >>> end;
> >>>
> >>>
> >>> Regarding the size of a dynamic array, like a string variable, the
> >>> variable (LogArgs in this case) is the size of a pointer (i.e. 4 bytes
> >>> for Win32). If the pointer is non-zero, it points to a structure which
> >>> includes the adjacent array elements preceded by a length.
> >>>
> >>> One thing to watch out for is that Getmem does not clear the allocated
> >>> memory, so LogData after the Getmem call will contain any old rubbish.
> >>> The reference to LogData.LogArgs in the assignment may be
> >>> dereferencing a non-zero pointer& attempting to use whatever it
> >>> contains.
> >>>
> >>> Cheers
> >>>
> >>>
> >>> On 25/08/2011 11:40 a.m., David Moorhouse (DUG) wrote:
> >>>> I have the following code snippet
> >>>>
> >>>> <code>
> >>>> type
> >>>> PConstArray = ^TConstArray;
> >>>> TConstArray = array of TVarRec;
> >>>>
> >>>> function CreateConstArray(const Elements: array of const):
> TConstArray;
> >>>>
> >>>> type
> >>>> TLogType = (ltError, ltWarn, ltInfo);
> >>>> PLogData = ^TLogData;
> >>>> TLogData = record
> >>>> LogType: TLogType;
> >>>> LogArgs: TConstArray;
> >>>> end;
> >>>>
> >>>> ....
> >>>>
> >>>> procedure TUserClass.Log(const LogType: TLogType; const Args: array of
> >>>> const );
> >>>> var
> >>>> LogData: PLogData;
> >>>> begin
> >>>> // size of record TLogData does not work
> >>>> GetMem(LogData, sizeof(TLogData));
> >>>> LogData.LogType := LogType;
> >>>> // blows up on next line
> >>>> LogData.LogArgs := CreateConstArray(Args);
> >>>> // ... do some other stuff with the LogData item finally calling
> >>>> FreeMem
> >>>> end;
> >>>>
> >>>> function CreateConstArray(const Elements: array of const):
> TConstArray;
> >>>> var
> >>>> I: Integer;
> >>>> begin
> >>>> SetLength(Result, Length(Elements));
> >>>> for I := Low(Elements) to High(Elements) do
> >>>> Result[I] := // assign a TVarRec here
> >>>> end;
> >>>> </code>
> >>>>
> >>>> The code that assigns the memory only assigns 8 bytes - and an access
> >>>> violation ensues. If I replace the call to "sizeof" with the number
> 16,
> >>>> the code works fine.
> >>>>
> >>>> My understanding of dynamic arrays was that the compiler created a 4
> >>>> byte
> >>>> field before the first element that contained the length of the array.
> >>>>
> >>>> So why does the sizeof function not reflect this ? And why do I
> >>>> need 16
> >>>> bytes not 12 (4 for LogType + 4 for length of array + 4 for array
> >>>> pointer)?
> >>>> Also regardless of the number of items in the open array parameter, 16
> >>>> bytes works, so it does not relate the length of the TConstArray.
> >>>>
> >>>> Your thoughts ?
> >>>>
> >>>> David
> >>>>
> >>>>****
>
> ** **
>
>
> _______________________________________________
> NZ Borland Developers Group - Delphi mailing list
> Post: delphi at listserver.123.net.nz
> Admin: http://delphi.org.nz/mailman/listinfo/delphi
> Unsubscribe: send an email to delphi-request at listserver.123.net.nz with
> Subject: unsubscribe****
>
> ** **
>
>
> _______________________________________________
> NZ Borland Developers Group - Delphi mailing list
> Post: delphi at listserver.123.net.nz
> Admin: http://delphi.org.nz/mailman/listinfo/delphi
> Unsubscribe: send an email to delphi-request at listserver.123.net.nz with Subject: unsubscribe
>
>
>
> _______________________________________________
> NZ Borland Developers Group - Delphi mailing list
> Post: delphi at listserver.123.net.nz
> Admin: http://delphi.org.nz/mailman/listinfo/delphi
> Unsubscribe: send an email to delphi-request at listserver.123.net.nz with
> Subject: unsubscribe
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://listserver.123.net.nz/pipermail/delphi/attachments/20110901/f8c85f3e/attachment-0001.html
More information about the Delphi
mailing list