Internal Data Formats (Delphi)
Contents
- 1 Integer Types
- 2 Integer Subrange Types
- 3 Character Types
- 4 Boolean Types
- 5 Enumerated Types
- 6 Real Types
- 7 Pointer Types
- 8 Short String Types
- 9 Long String Types
- 10 Wide String Types
- 11 Set Types
- 12 Static Array Types
- 13 Dynamic Array Types
- 14 Record Types
- 15 File Types
- 16 Procedural Types
- 17 Class Types
- 18 Class Reference Types
- 19 Variant Types
- 20 See Also
Go Up to Memory Management Index
The following topics describe the internal formats of Delphi data types.
Integer Types
Integer values have the following internal representation in Delphi.
Platform-Independent Unsigned Integer Types
Values of platform-independent integer types occupy the same number of bits on any platform.
Values of unsigned integer types always are positive and do not involve a Sign bit as do signed integer types. All bits of unsigned integer types occupy by the magnitude of the value and have no other meaning.
Byte, UInt8
Byte and UInt8 are 1-byte (8-bit) unsigned positive integer numbers. The Magnitude occupies all 8-bits.
Word and UInt16
Word and UInt16 are 2-byte (16-bit) unsigned integer numbers.
FixedUInt, Cardinal and UInt32
FixedUInt, Cardinal, and UInt32 are 4-byte (32-bit) unsigned integer numbers.
UInt64
UInt64 are 8-byte (64-bit) unsigned integer numbers.
Platform-Independent Signed Integer Types
Values of signed integer types represent a sign of a number by one leading sign bit, expressed by the most significant bit. The sign bit is 0 for a positive number, and 1 for a negative number. Other bits in a positive signed integer number are occupied by the magnitude. In a negative signed integer number, other bits are occupied by the two's complement representation of the magnitude of the value (absolute value).
To obtain the two's complement to a magnitude:
- Starting from the right, find the first '1'.
- Invert all of the bits to the left of that one.
For example:
Example 1 | Example 2 | |
---|---|---|
Magnitude | 0101010
|
1010101
|
2's Complement |
1010110
|
0101011
|
ShortInt, Int8
Shortint and Int8 are 1-byte (8-bit) signed integer numbers. The sign bit' occupies the most significant 7-th bit, the Magnitude or two's complement occupies other 7 bits.
SmallInt and Int16
SmallInt and Int16 are 2-byte (16-bit) signed integer numbers.
FixedInt, Integer and Int32
FixedInt, Integer, and Int32 are 4-byte (32-bit) signed integer numbers.
Int64
Int64 are 8-byte (64-bit) signed integer numbers.
Platform-Dependent Integer Types
The platform-dependent integer types are transformed to fit the bit size of the current target platform. On 64-bit platforms they occupy 64 bits, on 32-bit platforms they occupy 32 bits (except the LongInt and LongWord types). When the size of the target platform is the same as the CPU platform, then one platform-dependent integer number exactly matches the size of CPU registers. These types are often used when best performance is desired for a particular CPU type and operating system.
Unsigned Integer NativeUInt
NativeUInt is the platform-dependent unsigned integer type. The size and internal representation of NativeUInt depends on the current platform. On 32-bit platforms, NativeUInt is equivalent to the Cardinal type. On 64-bit platforms, NativeUInt is equivalent to the UInt64 type.
Signed Integer NativeInt
NativeInt is the platform-dependent signed integer type. The size and internal representation of NativeInt depends on the current platform. On 32-bit platforms, NativeInt is equivalent to the Integer type. On 64-bit platforms, NativeInt is equivalent to the Int64 type.
LongInt and LongWord
LongInt defines the signed integer type and the LongWord defines the unsigned integer type. LongInt and LongWord platform dependent integer types size are changed on each platforms, except for 64-bit Windows that remains unchanged (32-bits).
Size | ||
---|---|---|
32-bit platforms and 64-bit Windows platforms | 64-bit iOS platforms | |
LongInt | 32-bits (4 bytes) | 64-bits (8 bytes) |
LongWord |
32-bits (4 bytes) | 64-bits (8 bytes) |
- Note: 32-bit platforms in RAD Studio include 32-bit Windows, OSX32, 32-bit iOS, and Android.
On 64-bit iOS platforms, if you want to use:
Integer Subrange Types
When you use integer constants to define the minimum and maximum bounds of a subrange type, you define an integer subrange type. An integer subrange type represents a subset of the values in an integer type (called the base type). The base type is the smallest integer type that contains the specified range (contains both the minimum and maximum bounds).
The internal data format of an integer subrange type variable depends on its minimum and maximum bounds:
- If both bounds are within the range -128..127 (ShortInt), the variable is stored as a signed byte.
- If both bounds are within the range 0..255 (Byte), the variable is stored as an unsigned byte.
- If both bounds are within the range -32768..32767 (SmallInt), the variable is stored as a signed word.
- If both bounds are within the range 0..65535 (Word), the variable is stored as an unsigned word.
- If both bounds are within the range -2147483648..2147483647 (FixedInt and LongInt on 32-bit platforms and 64-bit Windows platforms), the variable is stored as a signed double word.
- If both bounds are within the range 0..4294967295 (FixedUInt and LongWord on 32-bit platforms and 64-bit Windows platforms), the variable is stored as an unsigned double word.
- If both bounds are within the range -2^63..2^63-1 (Int64 and LongInt on 64-bit iOS platforms), the variable is stored as a signed quadruple word.
- If both bounds are within the range 0..2^64-1 (UInt64 and LongWord on 64-bit iOS platforms), the variable is stored as an unsigned quadruple word.
Character Types
On the 32-bit and 64-bit platforms:
- Char and WideChar are stored as an unsigned word variable, normally using UTF-16 or Unicode encoding.
- AnsiChar type is stored as an unsigned byte. In Delphi 2007 and earlier, Char was represented as an AnsiChar. The character type used with Short Strings is always AnsiChar and is stored in unsigned byte values.
- The default long string type (string) is now UnicodeString, which is reference counted like an AnsiString, the former default long string type. Compatibility with older code may require the use of the AnsiString type.
- WideString is composed of WideChars like UnicodeString, but is not reference counted.
Boolean Types
A Boolean type is stored as a Byte, a ByteBool is stored as a Byte, a WordBool type is stored as a Word, and a LongBool is stored as a Longint.
A Boolean can assume the values 0 (False) and 1 (True). ByteBool, WordBool, and LongBool types can assume the values 0 (False) or nonzero (True).
Enumerated Types
An enumerated type is stored as an unsigned byte if the enumeration has no more than 256 values and the type was declared in the {$Z1} state (the default). If an enumerated type has more than 256 values, or if the type was declared in the {$Z2} state, it is stored as an unsigned word. If an enumerated type is declared in the {$Z4} state, it is stored as an unsigned double-word.
Real Types
The real types store the binary representation of a sign (+
or -
), an exponent, and a significand
. A real value has the form
+/- significand * 2^exponent
where the significand
has a single bit to the left of the binary decimal point (that is, 0 <= significand < 2
).
In the images that follow, the most significant bit is always on the left, and the least significant bit, on the right. The numbers at the top indicate the width (in bits) of each field, with the leftmost items stored at the highest addresses. For example, for a Real48 value, e
is stored in the first byte, f
in the following five bytes, and s
in the most significant bit of the last byte.
The Real48 type
On the 32-bit and 64-bit platforms, a 6-byte (48-bit) Real48 number is divided into three fields.
1 |
39 |
8 |
|
|
|
If 0 < e <= 255
, the value v
of the number is given by:
v = (-1)s * 2(e-129) * (1.f)
If e = 0
, then v = 0
.
The Real48 type cannot store denormals, NaNs, and infinities (Inf). Denormals become zero when stored in a Real48, while NaNs and infinities produce an overflow error if an attempt is made to store them in a Real48.
The Single type
On 32-bit and 64-bit platforms, a 4-byte (32-bit) Single number is divided into three fields.
1 |
8 |
23 |
|
|
|
The value v
of the number is given by:
- If
0 < e < 255
, thenv = (-1)s * 2(e-127) * (1.f)
- If
e = 0
andf <> 0
, thenv = (-1)s * 2(-126) * (0.f)
- If
e = 0
andf = 0
, thenv = (-1)s * 0
- If
e = 255
andf = 0
, thenv = (-1)s * Inf
- If
e = 255
andf <> 0
, thenv
is a NaN
The Double type
The Real type, in the current implementation, is equivalent to Double.
On 32-bit and 64-bit platforms, an 8-byte (64-bit) Double number is divided into three fields.
1 |
11 |
52 |
|
|
|
The value v
of the number is given by:
- If
0 < e < 2047
, thenv = (-1)s * 2(e-1023) * (1.f)
- If
e = 0
andf <> 0
, thenv = (-1)s * 2(-1022) * (0.f)
- If
e = 0
andf = 0
, thenv = (-1)s * 0
- If
e = 2047
andf = 0
, thenv = (-1)s * Inf
- If
e = 2047
andf <> 0
, thenv
is a NaN
The Extended type
Extended offers greater precision on 32-bit Intel platform than other real types, but is less portable. Be careful using Extended if you are creating data files to share across platforms. Be aware that:
On 32-bit Intel platform, an Extended number is represented as 10 bytes (80 bits). An Extended number is divided into four fields.
1 |
15 |
1 |
63 |
|
|
|
|
The value v
of the number is given by:
- If
0 <= e < 32767
, thenv = (-1)s * 2(e-16383) * (i.f)
- If
e = 32767
andf = 0
, thenv = (-1)s * Inf
- If
e = 32767
andf <> 0
, thenv
is a NaN
However, on the 64-bit Intel platform and ARM platform, the Extended type is an alias for Double, which is only 8 bytes. This difference can adversely affect numeric precision in floating-point operations. For more information, see Delphi Considerations for Multi-Device Applications. On MAC OS X systems, the size of Extended is 16 bytes in order to be compatible with BCCOSX.
The Comp type
An 8-byte (64-bit) Comp number is stored as a signed 64-bit integer.
The Currency type
An 8-byte (64-bit) Currency number is stored as a scaled and signed 64-bit integer with the 4 least significant digits implicitly representing 4 decimal places.
Pointer Types
On 32-bit platforms, a pointer type is stored in 4 bytes as a 32-bit address.
On 64-bit platforms, a pointer type is stored in 8 bytes as a 64-bit address.
The pointer value nil is stored as zero.
Short String Types
A ShortString string occupies as many bytes as its maximum length plus one. The first byte contains the current dynamic length of the string, and the following bytes contain the characters of the string.
The length byte and the characters are considered unsigned values. The maximum string length is 255 characters plus a length byte (string[255]).
Long String Types
A string variable of type UnicodeString or AnsiString occupies 4 bytes of memory on 32-bit platforms (and 8 bytes on 64-bit) that contain a pointer to a dynamically allocated string. When a string variable is empty (contains a zero-length string), the string pointer is nil and no dynamic memory is associated with the string variable. For a nonempty string value, the string pointer points to a dynamically allocated block of memory that contains the string value in addition to information describing the string. The tables below show the layout of a long-string memory block.
Format of UnicodeString data type (32-bit and 64-bit)
Field | CodePage | ElementSize | ReferenceCount | Length | String Data (ElementSized) |
Null Term |
---|---|---|---|---|---|---|
Offset |
|
|
|
|
|
|
Contents |
16-bit codepage of string data |
16-bit element size of string data |
32-bit reference-count |
Length in characters |
Character string of ElementSized data |
NULL character |
Numbers in the Offset row show offsets of fields, describing the string contents, from the string pointer, which points to the String Data field (offset = 0
), containing a block of memory that contains the actual string values.
The NULL character at the end of a string memory block is automatically maintained by the compiler and the built-in string handling routines. This makes it possible to typecast a string directly to a null-terminated string.
See also "New String Type: UnicodeString."
For string literals, the compiler generates a memory block with the same layout as a dynamically allocated string, but with a reference count of -1. String constants are treated the same way, the only difference from literals being that they are a pointer to a -1 reference counter block.
When a pointer to a string structure (source) is assigned to a string variable (destination), the reference counter dictates how this is done. Usually, the reference count is decreased for the destination and increased for the source, as both pointers, source and destination, will point to the same memory block after the assignment.
If the source reference count is -1 (string constant), a new structure is created with a reference count of 1. If the destination is not nil, the reference counter is decreased. If it reaches 0, the structure is deallocated from the memory. If the destination is nil, no additional actions are taken for it. The destination will then point to the new structure.
var
destination : String;
source : String;
...
destination := 'qwerty'; // reference count for the newly-created block of memory (containing the 'qwerty' string) pointed at by the "destination" variable is now 1
...
source := 'asdfgh'; // reference count for the newly-created block of memory (containing the 'asdfgh' string) pointed at by the "destination" variable is now 1
destination := source; // reference count for the memory block containing the 'asdfgh' string is now 2, and since reference count for the block of memory containing the 'qwerty' string is now 0, the memory block is deallocated.
If the source reference count is not -1, it is incremented and the destination will point to it.
var
destination, destination2, destination3: String;
destination := 'Sample String'; //reference count for the newly-created block of memory containing 'Sample string' is 1.
destination2 := destination; //reference count for the block of memory containing 'Sample string' is now 2.
destination3 := destination; //reference count for the block of memory containing 'Sample string' is now 3.
Wide String Types
On 32-bit platforms, a wide string variable occupies 4 bytes of memory (and 8 bytes on 64-bit) that contain a pointer to a dynamically allocated string. When a wide string variable is empty (contains a zero-length string), the string pointer is nil and no dynamic memory is associated with the string variable. For a nonempty string value, the string pointer points to a dynamically allocated block of memory that contains the string value in addition to a 32-bit length indicator. The table below shows the layout of a wide string memory block on Windows.
Wide string dynamic memory layout (32-bit and 64-bit)
Offset |
|
|
|
Contents |
32-bit length indicator |
Character string |
NULL character |
The string length is the number of bytes, so it is twice the number of wide characters contained in the string.
The NULL character at the end of a wide string memory block is automatically maintained by the compiler and the built-in string handling routines. This makes it possible to typecast a wide string directly to a null-terminated string.
Set Types
A set is a bit array where each bit indicates whether an element is in the set or not. The maximum number of elements in a set is 256, so a set never occupies more than 32 bytes. The number of bytes occupied by a particular set is equal to
(Max div 8) - (Min div 8) + 1
where Max
and Min
are the upper and lower bounds of the base type of the set. The byte number of a specific element E is
(E div 8) - (Min div 8)
and the bit number within that byte is
E mod 8
where E
denotes the ordinal value of the element. When possible, the compiler stores sets in CPU registers, but a set always resides in memory if it is larger than the platform-dependent integer type or if the program contains code that takes the address of the set.
Static Array Types
The memory that a static-array variable occupies is defined by the calculation of Length(array) * SizeOf(array[Low(array)]) in bytes. The static-array variable is entirely allocated as part of the parent data structure. A static array is stored as a contiguous sequence of elements of the component type of the array. The components with the lowest indexes are stored at the lowest memory addresses. A multidimensional array is stored with the rightmost dimension increasing first.
Dynamic Array Types
On the 32-bit platform, a dynamic-array variable occupies 4 bytes of memory (and 8 bytes on 64-bit) that contain a pointer to the dynamically allocated array. When the variable is empty (uninitialized) or holds a zero-length array, the pointer is nil and no dynamic memory is associated with the variable. For a nonempty array, the variable points to a dynamically allocated block of memory that contains the array in addition to a 32-bit (64-bit on Win64) length indicator and a 32-bit reference count. The table below shows the layout of a dynamic-array memory block.
Dynamic array memory layout (32-bit and 64-bit)
Offset 32-bit |
|
|
|
Offset 64-bit |
|
|
|
Contents |
32-bit reference-count |
32-bit or 64-bit on 64-bit platform |
Array elements |
Record Types
When a record type is declared in the {$A+}
state (the default), and when the declaration does not include a packed modifier, the type is an unpacked record type, and the fields of the record are aligned for efficient access by the CPU, and according to the platform. The alignment is controlled by the type of each field. Every data type has an inherent alignment, which is automatically computed by the compiler. The alignment can be 1, 2, 4, or 8, and represents the byte boundary on which a value of the type must be stored in order to provide the most efficient access. The table below lists the alignments for all data types.
Type alignment masks (32-bit only)
Type | Alignment |
---|---|
Ordinal types |
Size of the type (1, 2, 4, or 8) |
Real types |
2 for Real48, 4 for Single, 8 for Double and Extended |
Short string types |
1 |
Array types |
Same as the element type of the array |
Record types |
The largest alignment of the fields in the record |
Set types |
Size of the type if 1, 2, or 4, otherwise 1 |
All other types |
Determined by the $A directive |
To ensure proper alignment of the fields in an unpacked record type, the compiler inserts an unused byte before fields with an alignment of 2, and up to 3 unused bytes before fields with an alignment of 4, if required. Finally, the compiler rounds the total size of the record upward to the byte boundary specified by the largest alignment of any of the fields.
Implicit Packing of Fields with a Common Type Specification
Earlier versions of the Delphi compiler, such as Delphi 7 and earlier, implicitly applied packed alignment to fields that were declared together, that is, fields that have a common type specification. Newer compilers can reproduce the behavior if you specify the directive {$OLDTYPELAYOUT ON}. This directive byte-aligns (packs) the fields that have a common type specification, even if the declaration does not include the packed modifier and the record type is not declared in the {$A-}
state.
Thus, for example, given the following declaration:
{$OLDTYPELAYOUT ON}
type
TMyRecord = record
A, B: Extended;
C: Extended;
end;
{$OLDTYPELAYOUT OFF}
A
and B
are packed (aligned on byte boundaries) because the {$OLDTYPELAYOUT ON} directive is specified and because A
and B
share the same type specification. However, for the separately declared C
field, the compiler uses the default behavior and pads the structure with unused bytes to ensure the field appears on a quadword boundary.
When a record type is declared in the {$A-}
state, or when the declaration includes the packed modifier, the fields of the record are not aligned, but are instead assigned consecutive offsets. The total size of such a packed record is simply the size of all the fields. Because data alignment can change, it is a good idea to pack any record structure that you intend to write to disk or pass in memory to another module compiled using a different version of the compiler.
File Types
File types are represented as records. Typed files and untyped files occupy 592 bytes on 32-bit platforms and 616 bytes on 64-bit platforms, which are laid out as follows:
type
TFileRec = packed record
Handle: NativeInt;
Mode: word;
Flags: word;
case Byte of
0: (RecSize: Cardinal);
1: (BufSize: Cardinal;
BufPos: Cardinal;
BufEnd: Cardinal;
BufPtr: _PAnsiChr;
OpenFunc: Pointer;
InOutFunc: Pointer;
FlushFunc: Pointer;
CloseFunc: Pointer;
UserData: array[1..32] of Byte;
Name: array[0..259] of WideChar; );
end;
Text files occupy 730 bytes on Win 32 and 754 bytes on Win64, which are laid out as follows:
type
TTextBuf = array[0..127] of Char;
TTextRec = packed record
Handle: NativeInt;
Mode: word;
Flags: word;
BufSize: Cardinal;
BufPos: Cardinal;
BufEnd: Cardinal;
BufPtr: _PAnsiChr;
OpenFunc: Pointer;
InOutFunc: Pointer;
FlushFunc: Pointer;
CloseFunc: Pointer;
UserData: array[1..32] of Byte;
Name: array[0..259] of WideChar;
Buffer: TTextBuf; //
CodePage: Word;
MBCSLength: ShortInt;
MBCSBufPos: Byte;
case Integer of
0: (MBCSBuffer: array[0..5] of _AnsiChr);
1: (UTF16Buffer: array[0..2] of WideChar);
end;
Handle
contains the handle of the file (when the file is open).
The Mode
field can assume one of the values:
const
fmClosed = $D7B0;
fmInput= $D7B1;
fmOutput = $D7B2;
fmInOut= $D7B3;
where fmClosed
indicates that the file is closed, fmInput
and fmOutput
indicate a text file that has been reset (fmInput
) or rewritten (fmOutput
), fmInOut
indicates a typed or untyped file that has been reset or rewritten. Any other value indicates that the file variable is not assigned (and hence not initialized).
The UserData
field is available for user-written routines to store data in.
Name
contains the file name, which is a sequence of characters terminated by a null character (#0).
For typed files and untyped files, RecSize
contains the record length in bytes, and the Private
field is unused but reserved.
For text files, BufPtr
is a pointer to a buffer of BufSize
bytes, BufPos
is the index of the next character in the buffer to read or write, and BufEnd
is a count of valid characters in the buffer. OpenFunc
, InOutFunc
, FlushFunc
, and CloseFunc
are pointers to the I/O routines that control the file; see Device functions. Flags
determines the line break style as follows.
bit 0 clear |
LF line breaks |
bit 0 set |
CRLF line breaks |
All other Flags
bits are reserved for future use.
Procedural Types
On the 32-bit platform, a procedure pointer is stored as a 32-bit pointer to the entry point of a procedure or function. A method pointer is stored as a 32-bit pointer to the entry point of a method, followed by a 32-bit pointer to an object.
On the 64-bit platform, a procedure pointer is stored as a 64-bit pointer to the entry point of a procedure or function. A method pointer is stored as a 64-bit pointer to the entry point of a method, followed by a 64-bit pointer to an object.
Class Types
On the 32-bit platforms (Win32, OSX, iOS and Android), a class-type value is stored as a 32-bit pointer to an instance of the class (and as a 64-bit pointer on the 64-bit platform), which is called an object. The internal data format of an object resembles that of a record. The fields of the object are stored in order of declaration as a sequence of contiguous variables. Fields are always aligned, corresponding to an unpacked record type. Therefore, the alignment corresponds to the largest alignment of the fields in the object. Any fields inherited from an ancestor class are stored before the new fields defined in the descendent class.
On the 32-bit platforms, the first 4-byte field of every object (the first 8-byte field on the 64-bit platform) is a pointer to the virtual method table (VMT) of the class. There is exactly one VMT per class (not one per object); distinct class types, no matter how similar, never share a VMT. VMTs are built automatically by the compiler, and are never directly manipulated by a program. Pointers to VMTs, which are automatically stored by constructor methods in the objects they create, are also never directly manipulated by a program.
The layout of a VMT is shown in the following table. On the 32-bit platforms, at positive offsets, a VMT consists of a list of 32-bit method pointers (64-bit method pointers on the 64-bit platform)--one per user-defined virtual method in the class type--in order of declaration. Each slot contains the address of the corresponding entry point of the virtual method. This layout is compatible with a C++ v-table and with COM. At negative offsets, a VMT contains a number of fields that are internal to Delphi's implementation. Applications should use the methods defined in TObject to query this information, since the layout is likely to change in future implementations of the Delphi language.
Virtual method table layout
Offset Win32, OSX |
Offset Win64 |
Offset iOS/ARM, Android/ARM |
Offset iOS/Simulator |
Type | Description | Constant in System.pas |
---|---|---|---|---|---|---|
-88 |
-200 |
-108 |
-96 |
Pointer |
Pointer to virtual method table (or nil) |
vmtSelfPtr |
-84 |
-192 |
-104 |
-92 |
Pointer |
Pointer to interface table (or nil) |
vmtIntfTable |
-80 |
-184 |
-100 |
-88 |
Pointer |
Pointer to Automation information table (or nil) |
vmtAutoTable |
-76 |
-176 |
-96 |
-84 |
Pointer |
Pointer to instance initialization table (or nil) |
vmtInitTable |
-72 |
-168 |
-92 |
-80 |
Pointer |
Pointer to type information table (or nil) |
vmtTypeInfo |
-68 |
-160 |
-88 |
-76 |
Pointer |
Pointer to field definition table (or nil) |
vmtFieldTable |
-64 |
-152 |
-84 |
-72 |
Pointer |
Pointer to method definition table (or nil) |
vmtMethodTable |
-60 |
-144 |
-80 |
-68 |
Pointer |
Pointer to dynamic method table (or nil) |
vmtDynamicTable |
-56 |
-136 |
-76 |
-64 |
Pointer |
Pointer to short string containing class name |
vmtClassName |
-52 |
-128 |
-72 |
-60 |
Cardinal |
Instance size in bytes |
vmtInstanceSize |
-48 |
-120 |
-68 |
-56 |
Pointer |
Pointer to a pointer to ancestor class (or nil) |
vmtParent |
n/a |
n/a |
-64 |
-52 |
Pointer |
Entry point of __ObjAddRef method |
vmtObjAddRef |
n/a |
n/a |
-60 |
-48 |
Pointer |
Entry point of __ObjRelease method |
vmtObjRelease |
-44 |
-112 |
-56 |
-44 |
Pointer |
Entry point of Equals method |
vmtEquals |
-40 |
-104 |
-52 |
-40 |
Pointer |
Entry point of GetHashCode method |
vmtGetHashCode |
-36 |
-96 |
-48 |
-36 |
Pointer |
Entry point of ToString method |
vmtToString |
-32 |
-88 |
-44 |
-32 |
Pointer |
Pointer to entry point of SafecallException method (or nil) |
vmtSafeCallException |
-28 |
-80 |
-40 |
-28 |
Pointer |
Entry point of AfterConstruction method |
vmtAfterConstruction |
-24 |
-72 |
-36 |
-24 |
Pointer |
Entry point of BeforeDestruction method |
vmtBeforeDestruction |
-20 |
-64 |
-32 |
-20 |
Pointer |
Entry point of Dispatch method |
vmtDispatch |
-16 |
-56 |
-28 |
-16 |
Pointer |
Entry point of DefaultHandler method |
vmtDefaultHandler |
-12 |
-48 |
-24 |
-12 |
Pointer |
Entry point of NewInstance method |
vmtNewInstance |
-8 |
-40 |
-20 |
-8 |
Pointer |
Entry point of FreeInstance method |
vmtFreeInstance |
-4 |
-32 |
-16 |
-4 |
Pointer |
Entry point of Destroy destructor |
vmtDestroy |
0 |
0 |
0 |
0 |
Pointer |
Entry point of first user-defined virtual method |
|
4 |
8 |
4 |
4 |
Pointer |
Entry point of second user-defined virtual method |
Class Reference Types
On the 32-bit (Win32, OSX, iOS and Android) platform, a class-reference value is stored as a 32-bit pointer to the virtual method table (VMT) of a class.
On the 64-bit (Win64) platform, a class-reference value is stored as a 64-bit pointer to the virtual method table (VMT) of a class.
Variant Types
Variants rely on boxing and unboxing of data into an object wrapper, as well as Delphi helper classes to implement the variant-related RTL functions.
On the 32-bit platform, a variant is stored as a 16-byte record that contains a type code and a value (or a reference to a value) of the type given by the code. On the 64-bit platform, a variant is stored as a 24-byte record. The System and System.Variants units define constants and types for variants.
The TVarData type represents the internal structure of a Variant variable (on Windows, this is identical to the Variant type used by COM and the Win32 API). The TVarData type can be used in typecasts of Variant variables to access the internal structure of a variable. The TVarData record contains the following fields:
- The VType field of the TVarType type has the Word (16-bit) size. VType contains the type code of the variant in the lower 12 bits (the bits defined by the varTypeMask
= $FFF
constant). In addition, the varArray= $2000
bit may be set to indicate that the variant is an array, and the varByRef (= $4000
) bit may be set to indicate that the variant contains a reference as opposed to a value. - The Reserved1, Reserved2, and Reserved3 (Word size) fields are unused.
The contents of the remaining 8 bytes (32-bit platform) or 16 bytes (64-bit platform) of a TVarData record depend on the VType field as follows:
- If neither the varArray nor the varByRef bits are set, the variant contains a value of the given type.
- If the varArray bit is set, the variant contains a pointer to a TVarArray structure that defines an array. The type of each array element is given by the varTypeMask bits in the VType field.
- If the varByRef bit is set, the variant contains a reference to a value of the type given by the varTypeMask and varArray bits in the VType field.
The varString type code is private. Variants containing a varString value should never be passed to a non-Delphi function. On the Windows platform, Delphi's Automation support automatically converts varString variants to varOleStr variants before passing them as parameters to external functions.