内部データ形式(Delphi)

提供: RAD Studio
移動先: 案内検索

メモリ管理:インデックス への移動

以下のトピックでは、Delphi データ型の内部形式について説明します。

整数型

Delphi では、整数値の内部表現は以下のようになります。

プラットフォーム非依存の符号なし整数型

プラットフォームに依存しない整数型の値は、どのようなプラットフォームでも同じ数のビットを占めます。

符号なし整数型の値は常に正で、符号付き整数型のような符号ビットはありません。符号なし整数型の全ビットは絶対値で占められ、それ以外の意味を持っていません。

Byte、UInt8

ByteUInt8 は 1 バイト(8 ビット)の符号なし正整数です。絶対値(図の "Magnitude" 部分)は 8 ビット全部を占めます。

整数(符号なし 8 ビット)

Word、UInt16

WordUInt16 は 2 バイト(16 ビット)の符号なし整数です。

整数(符号なし 16 ビット)

FixedUInt、Cardinal、UInt32

FixedUIntCardinalUInt32 は 4 バイト(32 ビット)の符号なし整数です。

整数(符号なし 32 ビット)

UInt64

UInt64 は 8 バイト(64 ビット)の符号なし整数です。

整数(符号なし 64 ビット)

プラットフォーム非依存の符号付き整数型

符号付き整数型の値は、数の符号を先頭の符号ビット(最上位ビット)で表します。符号ビットは、正の数については 0 で、負の数については 1 です。正の符号付き整数のそれ以外のビットは、絶対値で占められます。負の符号付き整数では、符号以外のビットは、値の絶対値の 2 の補数表現で占められます。

絶対値の 2 の補数を取得するには:

  1. 右から調べて、最初の '1' を見つけます。
  2. そのビットの左側にあるビットをすべて反転させます。

以下に例を示します。

例 1 例 2
絶対値 0101010 1010101

2 の補数

1010110 0101011

ShortInt、Int8

ShortintInt8 は 1 バイト(8 ビット)の符号付き整数です。符号ビットが最上位の第 7 ビットを占め、絶対値または 2 の補数がそれ以外の 7 ビットを占めます。

整数(正の符号付き 8 ビット)
整数(負の符号付き 8 ビット)

SmallInt、Int16

SmallIntInt16 は 2 バイト(16 ビット)の符号付き整数です。

整数(正の符号付き 16 ビット)
整数(負の符号付き 16 ビット)

FixedInt、Integer、Int32

FixedIntIntegerInt32 は 4 バイト(32 ビット)の符号付き整数です。

整数(正の符号付き 32 ビット)
整数(負の符号付き 32 ビット)

Int64

Int64 は 8 バイト(64 ビット)の符号付き整数です。

整数(正の符号付き 64 ビット)
整数(負の符号付き 64 ビット)

プラットフォーム依存の整数型

プラットフォームに依存する整数型は、現在のターゲット プラットフォームのビット サイズに合わせて変換されます。これらの整数型は、64 ビット プラットフォームでは 64 ビットを占め、32 ビット プラットフォームでは 32 ビットを占めます(LongInt 型と LongWord 型以外)。 ターゲット プラットフォームのビット サイズが CPU プラットフォームと同じ場合は、プラットフォームに依存する一方の整数が CPU レジスタのサイズに正確に一致します。これらの型は、特定の CPU タイプおよびオペレーティング システムで最高のパフォーマンスが求められる場合によく使用されます。

符号なし整数 NativeUInt

NativeUInt は、プラットフォームに依存する符号なし整数型です。NativeUInt のサイズと内部表現は、現在のプラットフォームによって異なります。32 ビット プラットフォームでは、NativeUIntCardinal 型と同等です。64 ビット プラットフォームでは、NativeUIntUInt64 型と同等です。

符号付き整数 NativeInt

NativeInt は、プラットフォームに依存する符号付き整数型です。NativeInt のサイズと内部表現は、現在のプラットフォームによって異なります。32 ビット プラットフォームでは、NativeIntInteger 型と同等です。64 ビット プラットフォームでは、NativeIntInt64 型と同等です。

LongInt、LongWord

LongInt は符号付き整数型を定義し、LongWord は符号なし整数型を定義します。 LongIntLongWord のプラットフォーム依存の整数型のサイズは、(32 ビットに)変更されずそのままの 64 ビット Windows を除いて、プラットフォームごとに変更します。

サイズ
32 ビット プラットフォームおよび 64 ビット Windows プラットフォーム Windows 以外の 64 ビット プラットフォーム
LongInt 32 ビット(4 バイト) 64 ビット(8 バイト)

LongWord

32 ビット(4 バイト) 64 ビット(8 バイト)
メモ: RAD Studio での 32 ビット プラットフォームには、32 ビット Windows と 32 ビット Android があります。

Windows 以外の 64 ビット プラットフォームで、次を使用したい場合:

整数部分範囲型

整数定数を使用して部分範囲型の下限と上限を定義する場合は、整数部分範囲型を定義します。 整数部分範囲型は、整数型の値の部分集合を表します(基になる整数型は基底型と呼ばれます)。 基底型は、指定された範囲を含む(下限と上限も含まれる)最小の整数型です。

整数部分範囲型変数の内部データ形式は、その下限と上限によって異なります。

  • 両方の境界が -128 ~ 127(ShortInt)の間であれば、変数は符号付きバイトとして格納されます。
  • 両方の境界が 0 ~ 255(Byte)の間であれば、変数は符号なしバイトとして格納されます。
  • 両方の境界が -32768 ~ 32767(SmallInt)の間であれば、変数は符号付きワードとして格納されます。
  • 両方の境界が 0 ~ 65535(Word)の間であれば、変数は符号なしワードとして格納されます。
  • 両方の境界が -2147483648 ~ 2147483647(32 ビット プラットフォームおよび 64 ビット Windows プラットフォームの場合、FixedIntLongInt)の間であれば、変数は符号付きダブル ワードとして格納されます。
  • 両方の境界が 0 ~ 4294967295(32 ビット プラットフォームおよび 64 ビット Windows プラットフォームの場合、FixedUIntLongWord)の間であれば、変数は符号なしダブル ワードとして格納されます。
  • 両方の境界が -2^63 ~ 2^63-1(64 ビット iOS プラットフォームの場合、Int64LongInt)の間であれば、変数は符号付き 4 倍ワードとして格納されます。
  • 両方の境界が 0 ~ 2^64-1(64 ビット iOS プラットフォームの場合、UInt64LongWord)の間であれば、変数は符号なし 4 倍ワードとして格納されます。
メモ: "ワード" のサイズは 2 バイトです。

文字型

32 ビットおよび 64 ビット プラットフォームの場合:

  • CharWideChar は、符号なしワード変数として格納されます。通常は UTF-16 または Unicode エンコードが使用されます。
  • AnsiChar 型は、符号なしバイトとして格納されます。Delphi 2007(以前)では、CharAnsiChar として表現されていました。短い文字列を使用した文字型は常に AnsiChar で、符号なしバイト値として格納されます。
  • デフォルトの長い文字列型(string)は UnicodeString になり、以前の長い文字列型である、AnsiString と同様に参照がカウントされます。古いバージョンで作成したコードとの互換性を維持するために、場合により AnsiString 型を使用する必要があります。

論理型

Boolean 型はByte として、ByteBoolByte として、WordBool 型は Word として、LongBoolLongint として格納されます。

Boolean の値は、0(False)または 1(True)であるとみなすことができます。ByteBoolWordBool、および 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     

s

          f

    e


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           

s

     e

           f


この数の値 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                           

s

      e

                           f


この数の値 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                                  

s

         e

i

                                  f

この数の値 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 終端

オフセット

-12

-10

-8

-4

0..(長さ - 1)

長さ * 要素サイズ

内容

文字列データのコードページ(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.
メモ: 文字列変数は、参照カウントが の構造体を指すことはできません。構造体は、参照カウントが 0 になると必ずメモリから解放され、参照カウントが -1 の場合は変更できません。

ワイド文字列型

32 ビット プラットフォームでは、ワイド文字列変数のメモリ上のサイズは 4 バイト(64 ビットでは 8 バイト)で、その中に動的に割り当てられた文字列へのポインタが含まれます。ワイド文字列変数が空である(長さがゼロの文字列を含む)場合、文字列ポインタは nil になり、動的なメモリが文字列変数に関連付けられることはありません。文字列の値が空でない場合、文字列ポインタは動的に割り当てられたメモリ ブロックを指します。そのメモリ ブロックには文字列の値と 32 ビットの長さ指示子とが含まれます。次の表では、Windows におけるワイド文字列のメモリ ブロックのレイアウトを示します。

ワイド文字列の動的なメモリ レイアウト(32 ビットおよび 64 ビット)

オフセット

-4

0..(長さ - 1)

長さ

内容

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 ビット)

-8

-4

0..(長さ * 要素のサイズ - 1)

オフセット(64 ビット)

-12

-8

0..(長さ * 要素のサイズ - 1)

内容

参照カウント(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} 指令が指定されており、AB は型仕様が共通なので、AB はパック(バイト境界でアラインメント)されます。一方、別個に宣言されている 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 はファイルが閉じられていることを、fmInputfmOutput はテキスト ファイルがリセットされている(fmInput)か書き換えられている(fmOutput)ことを、fmInOut は型付きまたは型なしのファイルがリセットまたは書き換えられていることを、それぞれ示します。それ以外の値は、ファイル変数が割り当てられていない(つまり初期化されていない)ことを示します。

UserData フィールドは、ユーザーが作成したルーチンでデータを格納するためのものです。

Name にはファイル名を含みます。これは一続きの文字の最後に NULL 文字(#0)を付けたものです。

型付きファイルと型なしファイルの場合、RecSize にはレコード長のバイト数が入り、Private フィールドは未使用ですが予備として取ってあります。

テキスト ファイルの場合、BufPtrBufSize バイトのバッファに対するポインタ、BufPos は読み書きするバッファの次の文字を示すインデックス、BufEnd はバッファ中の有効な文字のカウントです。OpenFuncInOutFuncFlushFuncCloseFunc は、ファイルを制御する I/O ルーチンへのポインタです。これについてはデバイス関数を参照してください。Flags によって改行の形式が次のように決まります。

ビット 0 がクリアされている

LF の改行

ビット 0 がセットされている

CRLF の改行

Flags の残りの部分は今後のための予備です。

メモ: UnicodeString 型(デフォルトの Delphi の文字列型)を使用する場合は、Classes ユニット(TFileStreamTStreamReaderTStreamWriter など)の各種ストリーム型がさらに便利です。これは以前のファイル型(特にテキスト ファイル型)では、Unicode 機能が制限されているからです。

手続き型

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)ビットがセットされます。
  • Reserved1Reserved2Reserved3Word サイズ)のフィールドは未使用です。

TVarData レコードの残りの 8 バイト(32 ビット プラットフォーム)または 16 バイト(64 ビット プラットフォーム)の内容は、VType フィールドに応じて次のように変わります。

  • varArray ビットも varByRef ビットもセットされていない場合、バリアントには指定された型の値が含まれます。
  • varArray ビットがセットされている場合、バリアントには配列を定義する TVarArray 構造体へのポインタが含まれます。各配列要素の型は、VType フィールドの varTypeMask ビットによって決まります。
  • varByRef ビットがセットされている場合、バリアントには VType フィールドの varTypeMask ビットおよび varArray ビットで指定された型の値に対する参照が含まれます。

varString 型コードは private です。varString 値を含むバリアントは、Delphi 以外の関数に渡してはなりません。Windows プラットフォームでは、外部の関数にパラメータとして渡す前に、Delphi のオートメーション サポートが varString バリアントを varOleStr バリアントに自動的に変換します。

関連項目