[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