[DUG] [computing] Sizeof record gives error

David Moorhouse delphi at moorhouse.net.nz
Wed Aug 31 21:02:07 NZST 2011


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] *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 
> <mailto: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 
> <mailto: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 <mailto: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 <mailto: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 
> <mailto: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/20110831/cd76dede/attachment-0001.html 


More information about the Delphi mailing list