ポインタとポインタ型(Delphi)
目次
データ型、変数、定数:インデックス への移動
ポインタはメモリ アドレスを指す変数です。 ポインタに別の変数のアドレスが格納されている場合、その変数のメモリ内の場所またはそこに格納されているデータをそのポインタが指していると言います。 配列などの構造型の場合は、その構造における第 1 要素のアドレスがポインタに格納されます。 そのアドレスが既に取得されている場合、ポインタには第 1 要素のアドレスが格納されます。
ポインタは、それ自身が指すアドレスに格納されるデータの種類に応じて型指定されます。 汎用の Pointer 型が任意のデータへのポインタを表すことができるのに対して、より特殊化されたポインタ型は、特定の型のデータだけを指します。 PByte 型は、文字データ以外の任意のバイト データに使用されます。
32 ビット プラットフォームでは、ポインタは、32 ビット アドレスとして、メモリの 4 バイトを占めます。64 ビット プラットフォームでは、ポインタは、64 ビット アドレスとして、メモリの 8 バイトを占めます。
このトピックでは、以下について説明します。
- ポインタ型の概要
- Delphi でサポートされているポインタ型の宣言と使用
ポインタの概要
次の例は、ポインタの働きを示しています。
1 var 2 X, Y: Integer; // X and Y are Integer variables 3 P: ^Integer; // P points to an Integer 4 begin 5 X := 17; // assign a value to X 6 P := @X; // assign the address of X to P 7 Y := P^; // dereference P; assign the result to Y 8 end;
2 行目では、X と Y を Integer 型の変数として宣言します。 3 行目では、P を Integer 値へのポインタとして宣言します。つまり、P は X または Y の場所を指すことができます。 5 行目では X に値を代入し、6 行目では X のアドレス(@X と表す)を P に代入します。 最後に 7 行目では、P の指す場所(アドレス)に格納されている値(P^ と表す)を取得して Y に代入します。 このコードの実行後、X と Y の値はどちらも 17 になります。
ここで変数のアドレスを取得するのに使用した @ 演算子は、関数や手続きに対しても使用できます。 詳細については、「@ 演算子」および「ステートメントと式の中の手続き型」を参照してください。
キャレット記号 ^ には 2 つの用途があり、この例ではどちらの用途も示されています。 1 つは、型識別子の前に現れる場合です。
^typeName
キャレット記号は、型 typeName
の変数へのポインタの型であることを示します。
キャレット記号は、ポインタ
変数の後に現れます:
pointer^
キャレットは、ポインタ
を逆参照します。つまり、ポインタ
が指すメモリ アドレスに格納されている値を返します。
この例は、ある変数の値を別の変数にコピーする方法としては回りくどいように思われるかもしれません。単純な代入文でも同じことを実現できます。 しかし、ポインタが役に立つ理由はいくつかあります。 まず、ポインタを理解することで Delphi 言語の理解が深まります。コードに明示的にポインタが現れない場合でも、多くの場合、裏ではポインタが働いているからです。 動的に割り当てられる大きいメモリ ブロックが必要なデータ型では、ポインタを使用します。 たとえば、長い文字列を格納する変数は暗黙的にポインタであり、それはクラス インスタンス変数も同じです。 さらに、一部の高度なプログラミング技法ではポインタを使用する必要があります。
最後に、ポインタ以外に Delphi の厳密な型指定を回避する方法がない場合もあります。 汎用的な Pointer 型で変数を参照し、その Pointer 型を特定の型のポインタに型キャストしてから逆参照することで、任意の変数に格納されているデータを任意の型のデータとして扱うことができます。 たとえば、次のコードでは、実数型の変数に格納されているデータを整数型の変数に代入します。
type PInteger = ^Integer; var R: Single; I: Integer; P: Pointer; PI: PInteger; begin ... P := @R; PI := PInteger(P); I := PI^; end;
もちろん、実数と整数は異なる形式で格納されています。 この代入では、R から I に未処理のバイナリ データを変換せずにコピーするだけです。
@ 演算の結果を代入するだけでなく、いくつかの標準ルーチンを使用してポインタに値を代入することもできます。 手続き New および GetMem は、メモリ アドレスを既存のポインタに代入し、関数 Addr および Ptr は、指定されたアドレスまたは変数へのポインタを返します。
逆参照されたポインタは限定することができ、限定子として機能することもできます。たとえば、P1^.Data^ などとすることができます。
予約語 nil は任意のポインタに代入できる特殊な定数です。 nil をポインタに代入すると、そのポインタは何も参照しません。
ポインタを用いた拡張構文の使い方
{$EXTENDED} コンパイラ指令は、キャレット(^)の使用に影響を及ぼします。 {$X+} が有効な場合(デフォルト)は、ポインタの参照時にキャレットを省略できます。 しかし、ポインタの宣言時や、ポインタが別のポインタを指すときにあいまいさを解消するには、やはりキャレットが必要です。 詳細は、「拡張構文(Delphi)」を参照してください。
次の例に示すように、拡張構文が有効な場合は、ポインタの参照時にキャレットを省略できます:
{$X+} type PMyRec = ^TMyRec; TMyRec = record Data: Integer; end; var MyRec: PMyRec; begin New(MyRec); MyRec.Data := 42; {#1} end.
次の例に示すように、拡張構文が有効な場合は、ポインタの参照時にキャレットを省略できます:
MyRec^.Data := 42;
ポインタ型
次の構文を使用すると、どのような型へのポインタでも宣言できます。
type pointerTypeName = ^type
レコード型などのデータ型を定義するときは、その型へのポインタも定義しておくと便利なことがあります。 このようなポインタ型を定義すると、大きいメモリ ブロックをコピーせずにその型のインスタンスを簡単に操作できるようになります。
メモ: ポインタ型を宣言してから、そのポインタの指す型を宣言することができます。
さまざまな用途のために標準のポインタ型が用意されています。 最も柔軟性が高いのは Pointer 型で、任意の型のデータを指すことができます。 ただし、Pointer 型変数は逆参照できません。^ 記号を Pointer 型変数の後に付けると、コンパイル エラーになります。 Pointer 型変数で参照されるデータにアクセスするには、まず別のポインタ型に型キャストしてから逆参照します。
文字ポインタ
基本型 PAnsiChar および PWideChar は、それぞれ AnsiChar 値および WideChar 値へのポインタを表します。 汎用の PChar は Char へのポインタ(つまり、現在の実装では WideChar へのポインタ)を表します。 これらの文字ポインタは、NULL 終端文字列の操作に使用します。 「文字列型(Delphi)」の "NULL 終端文字列の取り扱い" を参照してください。
メモ: 文字ポインタ型でない型を PChar に型キャストしてポインタ算術演算を行わないでください。 代わりに PByte ポインタ型を使用してください。こちらの型は {$POINTERMATH ON} コンパイラ指令を付けて宣言されています。
バイト ポインタ
基本型 PByte は、文字データ以外の任意のバイト データへのポインタを表します。 この型は {$POINTERMATH ON}
コンパイラ指令で宣言されます。
function TCustomVirtualStringTree.InternalData(Node: PVirtualNode): Pointer; begin if (Node = FRoot) or (Node = nil) then Result := nil else Result := PByte(Node) + FInternalDataOffset; end;
型チェック済みポインタ
$T コンパイラ指令は、@ 演算子によって生成されるポインタ値の型を制御します。 この指令の形式は次のとおりです。
{$T+} or {$T-}
{$T-} の状態では、@ 演算子の結果の型は常に、他のすべてのポインタ型と互換性のある型なしポインタになります。 {$T+} の状態で @ が変数参照に適用される場合、適用結果の型は ^T になります。ここで、T は、その変数の型へのポインタとしか互換性がありません。
その他の標準ポインタ型
System
ユニットと SysUtils
ユニットには、よく使用される標準のポインタ型が多数宣言されています。
または、{POINTERMATH <ON|OFF>}
Delphi コンパイラ指令を使用してすべての型付きポインタでポインタ算術演算を有効にします。要素サイズでインクリメントやデクリメントできます。
System と SysUtils に宣言されているポインタ型(抜粋)
ポインタ型 | 指す変数の型 |
---|---|
TByteArray (declared in SysUtils). 動的に割り当てられたメモリを型キャストして配列にアクセスするのに使用されます。 | |
ShortString。 以前の PString 型を使用する従来のコードを移植する際に役に立ちます。 | |
TTextBuf (declared in SysUtils). TTextBuf は TTextRec ファイル レコードで使用される内部バッファ型です。 | |
TVarRecTVarRec(System に宣言) | |
TWordArray (declared in SysUtils). 動的に割り当てられたメモリを型キャストして 2 バイト値の配列にするために使用されます。 |