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 <[email protected]> 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:* [email protected] [ > mailto:[email protected]<[email protected]>] > *On Behalf Of *Jolyon Smith > *Sent:* Tuesday, 30 August 2011 12:04 p.m. > *To:* [email protected]; 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 <[email protected]> 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 <[email protected]> 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) <[email protected]> > 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: [email protected] > Admin: http://delphi.org.nz/mailman/listinfo/delphi > Unsubscribe: send an email to [email protected] with > Subject: unsubscribe**** > > ** ** > > > _______________________________________________ > NZ Borland Developers Group - Delphi mailing list > Post: [email protected] > Admin: http://delphi.org.nz/mailman/listinfo/delphi > Unsubscribe: send an email to [email protected] with > Subject: unsubscribe > > > > _______________________________________________ > NZ Borland Developers Group - Delphi mailing list > Post: [email protected] > Admin: http://delphi.org.nz/mailman/listinfo/delphi > Unsubscribe: send an email to [email protected] with > Subject: unsubscribe >
_______________________________________________ NZ Borland Developers Group - Delphi mailing list Post: [email protected] Admin: http://delphi.org.nz/mailman/listinfo/delphi Unsubscribe: send an email to [email protected] with Subject: unsubscribe
