内部データ形式(Delphi)
目次
メモリ管理:インデックス への移動
以下のトピックでは、Delphi データ型の内部形式について説明します。
整数型
Delphi では、整数値の内部表現は以下のようになります。
プラットフォーム非依存の符号なし整数型
プラットフォームに依存しない整数型の値は、どのようなプラットフォームでも同じ数のビットを占めます。
符号なし整数型の値は常に正で、符号付き整数型のような符号ビットはありません。符号なし整数型の全ビットは絶対値で占められ、それ以外の意味を持っていません。
Byte、UInt8
Byte と UInt8 は 1 バイト(8 ビット)の符号なし正整数です。絶対値(図の "Magnitude" 部分)は 8 ビット全部を占めます。
Word、UInt16
Word と UInt16 は 2 バイト(16 ビット)の符号なし整数です。
FixedUInt、Cardinal、UInt32
FixedUInt、Cardinal、UInt32 は 4 バイト(32 ビット)の符号なし整数です。
UInt64
UInt64 は 8 バイト(64 ビット)の符号なし整数です。
プラットフォーム非依存の符号付き整数型
符号付き整数型の値は、数の符号を先頭の符号ビット(最上位ビット)で表します。符号ビットは、正の数については 0 で、負の数については 1 です。正の符号付き整数のそれ以外のビットは、絶対値で占められます。負の符号付き整数では、符号以外のビットは、値の絶対値の 2 の補数表現で占められます。
絶対値の 2 の補数を取得するには:
- 右から調べて、最初の '1' を見つけます。
- そのビットの左側にあるビットをすべて反転させます。
以下に例を示します。
例 1 | 例 2 | |
---|---|---|
絶対値 | 0101010
|
1010101
|
2 の補数 |
1010110
|
0101011
|
ShortInt、Int8
Shortint と Int8 は 1 バイト(8 ビット)の符号付き整数です。符号ビットが最上位の第 7 ビットを占め、絶対値または 2 の補数がそれ以外の 7 ビットを占めます。
SmallInt、Int16
SmallInt と Int16 は 2 バイト(16 ビット)の符号付き整数です。
FixedInt、Integer、Int32
FixedInt、Integer、Int32 は 4 バイト(32 ビット)の符号付き整数です。
Int64
Int64 は 8 バイト(64 ビット)の符号付き整数です。
プラットフォーム依存の整数型
プラットフォームに依存する整数型は、現在のターゲット プラットフォームのビット サイズに合わせて変換されます。これらの整数型は、64 ビット プラットフォームでは 64 ビットを占め、32 ビット プラットフォームでは 32 ビットを占めます(LongInt 型と LongWord 型以外)。 ターゲット プラットフォームのビット サイズが CPU プラットフォームと同じ場合は、プラットフォームに依存する一方の整数が CPU レジスタのサイズに正確に一致します。これらの型は、特定の CPU タイプおよびオペレーティング システムで最高のパフォーマンスが求められる場合によく使用されます。
符号なし整数 NativeUInt
NativeUInt は、プラットフォームに依存する符号なし整数型です。NativeUInt のサイズと内部表現は、現在のプラットフォームによって異なります。32 ビット プラットフォームでは、NativeUInt は Cardinal 型と同等です。64 ビット プラットフォームでは、NativeUInt は UInt64 型と同等です。
符号付き整数 NativeInt
NativeInt は、プラットフォームに依存する符号付き整数型です。NativeInt のサイズと内部表現は、現在のプラットフォームによって異なります。32 ビット プラットフォームでは、NativeInt は Integer 型と同等です。64 ビット プラットフォームでは、NativeInt は Int64 型と同等です。
LongInt、LongWord
LongInt は符号付き整数型を定義し、LongWord は符号なし整数型を定義します。 LongInt と LongWord のプラットフォーム依存の整数型のサイズは、(32 ビットに)変更されずそのままの 64 ビット Windows を除いて、プラットフォームごとに変更します。
サイズ | ||
---|---|---|
32 ビット プラットフォームおよび 64 ビット Windows プラットフォーム | Windows 以外の 64 ビット プラットフォーム | |
LongInt | 32 ビット(4 バイト) | 64 ビット(8 バイト) |
LongWord |
32 ビット(4 バイト) | 64 ビット(8 バイト) |
Windows 以外の 64 ビット プラットフォームで、次を使用したい場合:
整数部分範囲型
整数定数を使用して部分範囲型の下限と上限を定義する場合は、整数部分範囲型を定義します。 整数部分範囲型は、整数型の値の部分集合を表します(基になる整数型は基底型と呼ばれます)。 基底型は、指定された範囲を含む(下限と上限も含まれる)最小の整数型です。
整数部分範囲型変数の内部データ形式は、その下限と上限によって異なります。
- 両方の境界が -128 ~ 127(ShortInt)の間であれば、変数は符号付きバイトとして格納されます。
- 両方の境界が 0 ~ 255(Byte)の間であれば、変数は符号なしバイトとして格納されます。
- 両方の境界が -32768 ~ 32767(SmallInt)の間であれば、変数は符号付きワードとして格納されます。
- 両方の境界が 0 ~ 65535(Word)の間であれば、変数は符号なしワードとして格納されます。
- 両方の境界が -2147483648 ~ 2147483647(32 ビット プラットフォームおよび 64 ビット Windows プラットフォームの場合、FixedInt と LongInt)の間であれば、変数は符号付きダブル ワードとして格納されます。
- 両方の境界が 0 ~ 4294967295(32 ビット プラットフォームおよび 64 ビット Windows プラットフォームの場合、FixedUInt と LongWord)の間であれば、変数は符号なしダブル ワードとして格納されます。
- 両方の境界が -2^63 ~ 2^63-1(64 ビット iOS プラットフォームの場合、Int64 と LongInt)の間であれば、変数は符号付き 4 倍ワードとして格納されます。
- 両方の境界が 0 ~ 2^64-1(64 ビット iOS プラットフォームの場合、UInt64 と LongWord)の間であれば、変数は符号なし 4 倍ワードとして格納されます。
文字型
32 ビットおよび 64 ビット プラットフォームの場合:
- AnsiChar 型は、符号なしバイトとして格納されます。Delphi 2007(以前)では、Char は AnsiChar として表現されていました。短い文字列を使用した文字型は常に AnsiChar で、符号なしバイト値として格納されます。
- デフォルトの長い文字列型(string)は UnicodeString になり、以前の長い文字列型である、AnsiString と同様に参照がカウントされます。古いバージョンで作成したコードとの互換性を維持するために、場合により AnsiString 型を使用する必要があります。
- WideString は UnicodeString と同様に WideChar で構成されますが、参照はカウントされません。
論理型
Boolean 型はByte として、ByteBool は Byte として、WordBool 型は Word として、LongBool は Longint として格納されます。
Boolean の値は、0(False)または 1(True)であるとみなすことができます。ByteBool、WordBool、および LongBool の型の値は、0(False)または 0 以外(True)であるとみなすことができます。
列挙型
列挙型は、列挙の値が 256 個以下で、かつ、型が {$Z1} 状態(デフォルト)で宣言されている場合には、符号なしバイトとして格納されます。列挙型の値が 256 個より多いか、型が {$Z2} 状態で宣言されている場合には、符号なしワードとして格納されます。列挙型が {$Z4} 状態で宣言されている場合には、符号なしダブル ワードとして格納されます。
実数型
実数型は、符号(+
または -
)、指数部、仮数部
の 2 進表現を格納したものです。実数値の形式は以下のとおりです。
+/- significand * 2^exponent
ここで、significand
(仮数部)は、2 進小数点の左側が 1 ビットです(つまり、0 <= significand < 2
)。
これ以降の画像では、最上位ビットは常に左側に、最下位ビットは常に右側にあります。上段の数は各フィールドの幅(ビット単位)を示し、最も左の項目が最も大きいアドレスに格納されます。たとえば、Real48 の値の場合、e
が第 1 バイトに、f
がその次の 5 バイトに、s
が最後のバイトの最上位ビットに、それぞれ格納されます。
Real48 型
32 ビットおよび 64 ビット プラットフォームでは、6 バイト(48 ビット)の Real48 型の数値は、3 つのフィールドに分かれています。
1 |
39 |
8 |
|
|
|
0 < e <= 255
である場合、この数の値 v
は次のようにして求められます。
v = (-1)s * 2(e-129) * (1.f)
e = 0
であれば、v = 0
になります。
Real48 型には、非正規化数、非数、無限大(Inf)を格納することはできません。非正規化数を Real48 に格納するとゼロになり、非数および無限大を Real48 に格納しようとするとオーバーフロー エラーが発生します。
単精度実数型
32 ビットおよび 64 ビット プラットフォームでは、4 バイト(32 ビット)の Single 型の数値は、3 つのフィールドに分かれています。
1 |
8 |
23 |
|
|
|
この数の値 v
は次のようにして求められます。
0 < e < 255
の場合、v = (-1)s * 2(e-127) * (1.f)
e = 0
かつf <> 0
の場合、v = (-1)s * 2(-126) * (0.f)
e = 0
かつf = 0
の場合、v = (-1)s * 0
e = 255
かつf = 0
の場合、v = (-1)s * Inf
e = 255
かつf <> 0
の場合、v
は非数
IEEE では、NaN を含むすべての比較演算が false を返すことを必須としています。 IEEE 754 は、NaN を含むすべての関係式に値を代入します。
C
の構文では、式 x != y
は True ですが、その他すべて:
x < y
x <= y
x == y
x >= y
x > y
は、x
または y
、もしくはその両方が NaN の場合に False となります。
倍精度実数型
Real 型は、現在の実装では、Double に相当します。
32 ビットおよび 64 ビット プラットフォームでは、8 バイト(64 ビット)の Double 型の数値は、3 つのフィールドに分かれています。
1 |
11 |
52 |
|
|
|
この数の値 v
は次のようにして求められます。
0 < e < 2047
の場合、v = (-1)s * 2(e-1023) * (1.f)
e = 0
かつf <> 0
の場合、v = (-1)s * 2(-1022) * (0.f)
e = 0
かつf = 0
の場合、v = (-1)s * 0
e = 2047
かつf = 0
の場合、v = (-1)s * Inf
e = 2047
かつf <> 0
の場合、v
は非数
IEEE では、NaN を含むすべての比較演算が false を返すことを必須としています。 IEEE 754 は、NaN を含むすべての関係式に値を代入します。
C
の構文では、式 x != y
は True ですが、その他すべて:
x < y
x <= y
x == y
x >= y
x > y
は、x
または y
、もしくはその両方が NaN の場合に False となります。
拡張精度実数型
Extended を使用すると、32 ビット Intel プラットフォーム上で他の実数型よりも精度が高くなりますが、移植性は低下します。 クロス プラットフォームで共有するデータ ファイルを作成する場合は、Extended の使用に注意してください。 次の点に留意してください:
32 ビット Windows プラットフォームでは、Extended 型の数値は 10 バイト(80 ビット)として表されます。 Extended 型の数値は以下の 4 つのフィールドに分けられます。
1 |
15 |
1 |
63 |
|
|
|
|
この数の値 v
は次のようにして求められます。
0 <= e < 32767
の場合、v = (-1)s * 2(e-16383) * (i.f)
e = 32767
かつf = 0
の場合、v = (-1)s * Inf
e = 32767
かつf <> 0
の場合、v
は非数
Windows 以外の 64 ビット Intel プラットフォーム(64 ビット Linux または 64 ビット macOS Intel)では、たとえデータ ビットが 10 バイトしか使用しない場合でも、Extended 型は 16 バイトとなります。
しかしながら、64 ビット Windows Intel プラットフォームと ARM プラットフォーム(64 ビット macOS ARM、31ビット Android、64 ビット Android、64 ビット iOS)では、Extended 型は Double のエイリアスで、8 バイトしかありません。この違いは、浮動小数点演算の数値精度に悪影響を及ぼすおそれがあります。 詳細については、「Delphi におけるマルチデバイス アプリケーションについての考慮事項」を参照してください。
計算型
8 バイト(64 ビット)の Comp 型の数値は、符号付き 64 ビット整数として格納されます。
通貨型
8 バイト(64 ビット)の Currency 型の数は、位取りのある符号付き 64 ビット整数として格納され、最下位の 4 桁が暗黙的に小数点以下の 4 桁を表します。
ポインタ型
32 ビット プラットフォームでは、ポインタ型は、32 ビット アドレスとして 4 バイトで格納されます。
64 ビット プラットフォームでは、ポインタ型は、64 ビット アドレスとして 8 バイトで格納されます。
ポインタ値 nil はゼロとして格納されます。
短い文字列型
ShortString 文字列のサイズは、文字列の最大の長さに 1 バイトを足したものになります。最初の 1 バイトには現在の文字列の動的な長さが、その後のバイトには文字列の文字が含まれます。
長さを表すバイトと文字は、符号なしの値として扱われます。最大文字列長は、255 文字に長さを表す 1 バイトを加えたもの(string[255])になります。
長い文字列型
UnicodeString 型または AnsiString 型の文字列変数は、32 ビット プラットフォームでは 4 バイト(64 ビットでは 8 バイト)のメモリ領域を占め、その中に、動的に割り当てられた文字列へのポインタが格納されます。文字列変数が空である(長さゼロの文字列が格納されている)場合、文字列ポインタは nil になり、その文字列変数には動的メモリは関連付けられません。文字列値が空でない場合、文字列ポインタは動的に割り当てられたメモリ ブロックを指し、そこに文字列値のほか、その文字列についての情報が格納されています。長い文字列を格納するメモリ ブロックのレイアウトを以下の表に示します。
UnicodeString データ型(32 ビットおよび 64 ビット)の形式
フィールド | コードページ | 要素サイズ | 参照カウント | 長さ | 文字列データ (要素サイズを考慮) |
NULL 終端 |
---|---|---|---|---|---|---|
オフセット |
|
|
|
|
|
|
内容 |
文字列データのコードページ(16 ビット) |
文字列データの要素サイズ(16 ビット) |
参照カウント(32 ビット) |
文字数 |
要素サイズを考慮した文字列データ |
NULL 文字 |
オフセット行の数値は、文字列ポインタからの各フィールド(文字列の内容を記述する情報)のオフセットを示しています。文字列ポインタは文字列データ フィールド(オフセット = 0
)を指しており、そこに、実際の文字列値を表すメモリ ブロックが格納されています。
文字列のメモリ ブロックの末尾にある NULL 文字は、コンパイラや組み込みの文字列処理ルーチンで自動的に維持管理されます。このため、文字列を直接 NULL 終端文字列に型キャストすることができます。
「新しい文字列型: UnicodeString」も参照してください。
文字列リテラルについては、動的に割り当てられた文字列と同じレイアウトのメモリ ブロックがコンパイラによって生成されますが、参照カウントは -1 になります。文字列定数も同じように扱われますが、それらが参照カウント -1 のブロックを指すポインタである点だけがリテラルと異なります。
文字列構造体(代入元)へのポインタが文字列変数(代入先)に代入されるとき、それがどう行われるかは参照カウントで決まります。通常、代入先の参照カウントは減り、代入元の参照カウントは増えます。このとき、両ポインタ(代入元と代入先)は、代入の後には、同じメモリ ブロックを指しています。
代入元の参照カウントが -1(文字列定数)の場合は、参照カウントが 1 の新しい構造体が作成されます。代入先が nil でなければ、参照カウントは減ります。参照カウントが 0 になると、構造体はメモリから解放されます。代入先が nil の場合、それに対しては、もうそれ以上操作は行われません。 代入先は、その後、この新しい構造体を指します。
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.
代入元の参照カウントが -1 でなければ、その参照カウントがインクリメントされ、代入先は代入元を指します。
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.
ワイド文字列型
32 ビット プラットフォームでは、ワイド文字列変数のメモリ上のサイズは 4 バイト(64 ビットでは 8 バイト)で、その中に動的に割り当てられた文字列へのポインタが含まれます。ワイド文字列変数が空である(長さがゼロの文字列を含む)場合、文字列ポインタは nil になり、動的なメモリが文字列変数に関連付けられることはありません。文字列の値が空でない場合、文字列ポインタは動的に割り当てられたメモリ ブロックを指します。そのメモリ ブロックには文字列の値と 32 ビットの長さ指示子とが含まれます。次の表では、Windows におけるワイド文字列のメモリ ブロックのレイアウトを示します。
ワイド文字列の動的なメモリ レイアウト(32 ビットおよび 64 ビット)
オフセット |
|
|
|
内容 |
32 ビットの長さ指示子 |
文字列 |
NULL 文字 |
文字列の長さはバイト数なので、文字列に含まれるワイド文字の数の 2 倍になります。
ワイド文字列のメモリ ブロックの最後にある NULL 文字は、コンパイラや組み込みの文字列処理ルーチンによって自動的に保守されます。このため、ワイド文字列を NULL 終端文字列に直接に型キャストすることが可能です。
集合型
集合はビット配列であり、その各ビットはある要素が集合に含まれているかどうかを示します。集合に含まれる要素の最大数は 256 であるため、1 つの集合が 32 バイトよりも大きい領域を占めることはありません。特定の集合で占有されるバイト数は以下に等しくなります。
(Max div 8) - (Min div 8) + 1
ここで、Max
および Min
は、集合の基底型の上限および下限です。特定の要素 E のバイト数は以下のとおりです。
(E div 8) - (Min div 8)
また、そのバイト内のビット数は以下のとおりです。
E mod 8
ここで、E
は要素の順序値を示します。コンパイラは可能な限り、集合を CPU レジスタに格納しますが、集合がプラットフォーム依存の整数型より大きい場合や、集合のアドレスを取るコードがプログラムに含まれている場合には、集合は必ずメモリ上に置かれます。
静的配列型
静的配列が占めるメモリは、Length(array) * SizeOf(array[Low(array)]) の計算によって、バイト数で定義されます。静的配列の変数は、親のデータ構造体の一部として全体を確保されます。静的配列は、配列のコンポーネント型の要素群の連続したシーケンスとして格納されます。一番小さいインデックスを持つコンポーネントが、一番小さいメモリ アドレスに格納されます。多次元配列の場合は、一番右の次元が先に増加するように格納されます。
動的配列型
32 ビット プラットフォームでは、動的配列変数のメモリ上のサイズは 4 バイト(64 ビットでは 8 バイト)で、その中に動的に割り当てられた配列へのポインタが含まれます。変数が空である(初期化されていない)、または変数が持っている配列の長さがゼロである場合、ポインタは nil になり、動的なメモリが変数に関連付けられることはありません。配列が空でない場合、変数は動的に割り当てられたメモリ ブロックを指します。そのメモリ ブロックには、配列と、32 ビットの長さ指示子(Win64 では 64 ビット)、32 ビットの参照カウントが含まれます。次の表では、動的配列のメモリ ブロックのレイアウトを示します。
動的配列のメモリ レイアウト(32 ビットおよび 64 ビット)
オフセット(32 ビット) |
|
|
|
オフセット(64 ビット) |
|
|
|
内容 |
参照カウント(32 ビット) |
32 ビット(64 ビット プラットフォームでは 64 ビット)の |
配列の要素 |
レコード型
レコード型が {$A+}
状態(デフォルト)で宣言されていて、宣言に packed 修飾子が含まれていない場合、その型はアンパック レコード型となり、レコードのフィールドは CPU から効率的にアクセスできるように、かつ、プラットフォームに従ってアラインメントされます。このアラインメントは、各フィールドの型によって制御されます。どのデータ型にも固有のアラインメントがあり、それはコンパイラによって自動的に計算されます。アラインメントは 1、2、4、8 のいずれかであり、最も効率的なアクセスを実現するために必要な、型の値を格納する際のバイト境界を表します。次の表では、すべてのデータ型のアラインメントを一覧で示します。
型のアラインメント マスク(32 ビットの場合のみ)
型 | アラインメント |
---|---|
順序型 |
型のサイズ(1、2、4、または 8) |
実数型 |
Real48 は 2、Single は 4、Double および Extended は 8 |
短い文字列型 |
1 |
配列型 |
配列の要素の型と同じ |
レコード型 |
レコード内の各フィールドのアラインメントのうち、最も大きいもの |
集合型 |
型のサイズが 1、2、または 4 の場合は型のサイズ、それ以外の場合は 1 |
その他のすべての型 |
$A 指令によって決まる |
アンパック レコード型のフィールドが正しくアラインメントされるように、コンパイラは必要に応じて、アラインメントが 2 のフィールドの前には使用されない 1 バイトを、アラインメントが 4 のフィールドの前には使用されない最大 3 バイトを挿入します。最後にコンパイラは、フィールドのアラインメントのうちで最大のものによって決まるバイト境界まで、レコードの合計サイズを切り上げます。
型仕様が共通なフィールドの暗黙的パッキング
Delphi コンパイラの以前のバージョン(たとえば、Delphi 7 やそれ以前のもの)では、一緒に宣言されているフィールドつまり型仕様が共通のフィールドにもパック アラインメントが暗黙に適用されていました。より新しいコンパイラでは、{$OLDTYPELAYOUT ON} 指令が指定された場合、この動作を再現できます。この指令が指定されると、たとえ宣言に packed 修飾子が含まれておらず、レコード型が {$A-}
状態で宣言されていなくても、型仕様が共通のフィールドはバイト境界でアラインメント(つまりパック)されます。
たとえば、次のような宣言があるとします。
{$OLDTYPELAYOUT ON}
type
TMyRecord = record
A, B: Extended;
C: Extended;
end;
{$OLDTYPELAYOUT OFF}
{$OLDTYPELAYOUT ON} 指令が指定されており、A
と B
は型仕様が共通なので、A
と B
はパック(バイト境界でアラインメント)されます。一方、別個に宣言されている C
フィールドについては、コンパイラはデフォルトの動作を行い、フィールドが必ずクワッドワード境界に配置されるように、使用されないバイト領域で構造体をパディングします。
レコード型が {$A-}
状態で宣言されていたり、宣言に packed 修飾子が含まれている場合、レコードのフィールドはアラインメントされず、連続したオフセットが割り当てられます。そのようなパック レコードの合計サイズは、単純にすべてのフィールドのサイズを合計したものとなります。データ アラインメントは変更される可能性があるため、ディスクに書き出したり、バージョンの異なるコンパイラでコンパイルされた別のモジュールにメモリ内で渡したりする予定のあるレコード構造は、パックにする方がよいでしょう。
ファイル型
ファイル型はレコードとして表現されます。型付きファイルと型なしファイルのサイズは 32 ビット プラットフォームでは 592 バイト、64 ビット プラットフォームでは 616 バイトで、次のようにレイアウトされます。
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;
テキスト ファイルのサイズは Win32 では 730 バイト、Win64 では 754 バイトで、次のようにレイアウトされます。
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
にはファイルのハンドルが含まれます(ファイルが開いている場合)。
Mode
フィールドは次のいずれかの値になります。
const
fmClosed = $D7B0;
fmInput= $D7B1;
fmOutput = $D7B2;
fmInOut= $D7B3;
fmClosed
はファイルが閉じられていることを、fmInput
と fmOutput
はテキスト ファイルがリセットされている(fmInput
)か書き換えられている(fmOutput
)ことを、fmInOut
は型付きまたは型なしのファイルがリセットまたは書き換えられていることを、それぞれ示します。それ以外の値は、ファイル変数が割り当てられていない(つまり初期化されていない)ことを示します。
UserData
フィールドは、ユーザーが作成したルーチンでデータを格納するためのものです。
Name
にはファイル名を含みます。これは一続きの文字の最後に NULL 文字(#0)を付けたものです。
型付きファイルと型なしファイルの場合、RecSize
にはレコード長のバイト数が入り、Private
フィールドは未使用ですが予備として取ってあります。
テキスト ファイルの場合、BufPtr
は BufSize
バイトのバッファに対するポインタ、BufPos
は読み書きするバッファの次の文字を示すインデックス、BufEnd
はバッファ中の有効な文字のカウントです。OpenFunc
、InOutFunc
、FlushFunc
、CloseFunc
は、ファイルを制御する I/O ルーチンへのポインタです。これについてはデバイス関数を参照してください。Flags
によって改行の形式が次のように決まります。
ビット 0 がクリアされている |
LF の改行 |
ビット 0 がセットされている |
CRLF の改行 |
Flags
の残りの部分は今後のための予備です。
手続き型
32 ビット プラットフォームでは、手続きポインタは、手続きまたは関数のエントリ ポイントを指す 32 ビット ポインタとして格納されます。メソッド ポインタは、メソッドのエントリ ポイントを指す 32 ビット ポインタの後に、オブジェクトを指す 32 ビット ポインタを続けたものとして格納されます。
64 ビット プラットフォームでは、手続きポインタは、手続きまたは関数のエントリ ポイントを指す 64 ビット ポインタとして格納されます。メソッド ポインタは、メソッドのエントリ ポイントを指す 64 ビット ポインタの後に、オブジェクトを指す 64 ビット ポインタを続けたものとして格納されます。
クラス型
32 ビット プラットフォーム(Win32、Android)では、クラス型の値は、クラスのインスタンスに 32 ビット ポインタ(64 ビット プラットフォームでは 64 ビット ポインタとして)として格納され、オブジェクト と呼ばれます。 オブジェクトの内部データ形式は、レコードの場合と似ています。 オブジェクトの各フィールドは、連続した一連の変数として宣言の順に格納されます。 フィールドは必ず、アンパック レコード型と同じようにアラインメントされます。 このため、アライメントは オブジェクト でのフィールド群の最大アライメントに対応します。 上位クラスから継承したフィールドはどれも、下位クラスで新しく定義されたフィールドより前に格納されます。
32 ビット プラットフォームでは、どのオブジェクトも最初の 4 バイト フィールド(64 ビット プラットフォームでは最初の 8 バイト フィールド)は、クラスの仮想メソッド テーブル(VMT)へのポインタになっています。 VMT はクラスごとに(オブジェクトごとではなく)必ず 1 つ存在します。いくら似ていても、異なるクラス型が VMT を共有することはありません。 VMT はコンパイラが自動的に構築するものであり、プログラムで直接操作することはありません。 VMT へのポインタも、作成するオブジェクトのコンストラクタ メソッドで自動的に格納されるもので、プログラムで直接操作することはありません。
VMT のレイアウトを下の表に示します。 32 ビット プラットフォームでは、VMT のオフセットが正の部分は、32 ビットのメソッド ポインタのリストになっています(64 ビット プラットフォームでは 64 ビットのメソッド ポインタ)。ポインタは、クラス型に含まれるユーザー定義の仮想メソッドそれぞれについて 1 つ、宣言の順番に並んでいます。 各スロットには、対応する仮想メソッドのエントリ ポイントのアドレスが含まれます。 このレイアウトは、C++ の v-table や COM と互換性があります。 VMT のオフセットが負の部分には、Delphi の実装が内部的に使用するいくつかのフィールドが含まれます。 アプリケーションでこの情報を問い合わせるには、TObject で定義されたメソッドを使用してください。Delphi 言語の今後の実装でレイアウトが変更される可能性があるためです。
仮想メソッド テーブルのレイアウト
オフセット Win32、macOS |
オフセット Win64 |
オフセット iOS/ARM、Android/ARM |
型 | 説明 | System.pas 内の定数 |
---|---|---|---|---|---|
-88 |
-200 |
-108 |
Pointer |
仮想メソッド テーブルへのポインタ(または nil) |
vmtSelfPtr |
-84 |
-192 |
-104 |
Pointer |
インターフェイス テーブルへのポインタ(または nil) |
vmtIntfTable |
-80 |
-184 |
-100 |
Pointer |
オートメーション情報テーブルへのポインタ(または nil) |
vmtAutoTable |
-76 |
-176 |
-96 |
Pointer |
インスタンス初期化テーブルへのポインタ(または nil) |
vmtInitTable |
-72 |
-168 |
-92 |
Pointer |
型情報テーブルへのポインタ(または nil) |
vmtTypeInfo |
-68 |
-160 |
-88 |
Pointer |
フィールド定義テーブルへのポインタ(または nil) |
vmtFieldTable |
-64 |
-152 |
-84 |
Pointer |
メソッド定義テーブルへのポインタ(または nil) |
vmtMethodTable |
-60 |
-144 |
-80 |
Pointer |
動的メソッド テーブルへのポインタ(または nil) |
vmtDynamicTable |
-56 |
-136 |
-76 |
Pointer |
クラス名を格納した短い文字列へのポインタ |
vmtClassName |
-52 |
-128 |
-72 |
Cardinal |
インスタンスのサイズ(単位はバイト) |
vmtInstanceSize |
-48 |
-120 |
-68 |
Pointer |
上位クラスへのポインタへのポインタ(または nil) |
vmtParent |
該当なし |
該当なし |
-64 |
Pointer |
__ObjAddRef メソッドのエントリ ポイント |
vmtObjAddRef |
該当なし |
該当なし |
-60 |
Pointer |
__ObjRelease メソッドのエントリ ポイント |
vmtObjRelease |
-44 |
-112 |
-56 |
Pointer |
Equals メソッドのエントリ ポイント |
vmtEquals |
-40 |
-104 |
-52 |
Pointer |
GetHashCode メソッドのエントリ ポイント |
vmtGetHashCode |
-36 |
-96 |
-48 |
Pointer |
ToString メソッドのエントリ ポイント |
vmtToString |
-32 |
-88 |
-44 |
Pointer |
SafecallException メソッドのエントリ ポイントへのポインタ(または nil) |
vmtSafeCallException |
-28 |
-80 |
-40 |
Pointer |
AfterConstruction メソッドのエントリ ポイント |
vmtAfterConstruction |
-24 |
-72 |
-36 |
Pointer |
BeforeDestruction メソッドのエントリ ポイント |
vmtBeforeDestruction |
-20 |
-64 |
-32 |
Pointer |
Dispatch メソッドのエントリ ポイント |
vmtDispatch |
-16 |
-56 |
-28 |
Pointer |
DefaultHandler メソッドのエントリ ポイント |
vmtDefaultHandler |
-12 |
-48 |
-24 |
Pointer |
NewInstance メソッドのエントリ ポイント |
vmtNewInstance |
-8 |
-40 |
-20 |
Pointer |
FreeInstance メソッドのエントリ ポイント |
vmtFreeInstance |
-4 |
-32 |
-16 |
Pointer |
Destroy デストラクタのエントリ ポイント |
vmtDestroy |
0 |
0 |
0 |
Pointer |
最初のユーザー定義仮想メソッドのエントリ ポイント |
|
4 |
8 |
4 |
Pointer |
2 つめのユーザー定義仮想メソッドのエントリ ポイント |
クラス参照型
32 ビット プラットフォーム(Win32、Android)では、クラス参照の値は、クラスの仮想メソッド テーブル(VMT)への 32 ビット ポインタとして格納されます。
64 ビット プラットフォーム(Win64、64 ビット Linux、64 ビット iOS、64 ビット Android)では、クラス参照の値は、クラスの仮想メソッド テーブル(VMT)への 64 ビット ポインタとして格納されます。
バリアント型
バリアントを使うには、データをオブジェクト ラッパーにボックス化したりボックス化解除しなければならず、さらに Delphi のヘルパー クラスでバリアントに関する RTL 関数群を実装しなければなりません。
32 ビット プラットフォームでは、バリアントは、コードで決められた型の型コードと値(または値への参照)を含む 16 バイト レコードとして格納されます。64 ビット プラットフォームでは、バリアントは 24 バイト レコードとして格納されます。System および System.Variants のユニットでは、バリアントの定数と型を定義しています。
TVarData 型は Variant 変数の内部構造を表します(Windows では、これは COM や Win32 API が使用する Variant 型と同じです)。TVarData 型を使って Variant 変数の型キャストを行い、変数の内部構造にアクセスすることができます。TVarData レコードには次のフィールドが含まれます。
- TVarType 型の VType フィールドは、Word(16 ビット)サイズです。VType は、下位 12 ビット(varTypeMask
= $FFF
定数で定義されたビット)のバリアントの型コードを含みます。さらに、バリアントが配列の場合には varArray= $2000
ビットがセットされ、バリアントに値ではなく参照が含まれる場合には varByRef(= $4000
)ビットがセットされます。 - Reserved1、Reserved2、Reserved3(Word サイズ)のフィールドは未使用です。
TVarData レコードの残りの 8 バイト(32 ビット プラットフォーム)または 16 バイト(64 ビット プラットフォーム)の内容は、VType フィールドに応じて次のように変わります。
- varArray ビットも varByRef ビットもセットされていない場合、バリアントには指定された型の値が含まれます。
- varArray ビットがセットされている場合、バリアントには配列を定義する TVarArray 構造体へのポインタが含まれます。各配列要素の型は、VType フィールドの varTypeMask ビットによって決まります。
- varByRef ビットがセットされている場合、バリアントには VType フィールドの varTypeMask ビットおよび varArray ビットで指定された型の値に対する参照が含まれます。
varString 型コードは private です。varString 値を含むバリアントは、Delphi 以外の関数に渡してはなりません。Windows プラットフォームでは、外部の関数にパラメータとして渡す前に、Delphi のオートメーション サポートが varString バリアントを varOleStr バリアントに自動的に変換します。