ポインタとポインタ型(Object Pascal)

提供: Appmethod Topics
移動先: 案内検索

データ型、変数、定数:インデックス への移動

ポインタはメモリ アドレスを指す変数です。ポインタに別の変数のアドレスが格納されている場合、その変数のメモリ内の場所またはそこに格納されているデータをそのポインタが指していると言います。配列などの構造型の場合は、その構造における第 1 要素のアドレスがポインタに格納されます。そのアドレスが既に取得されている場合、ポインタには第 1 要素のアドレスが格納されます。

ポインタは、それ自身が指すアドレスに格納されるデータの種類に応じて型指定されます。汎用の Pointer 型が任意のデータへのポインタを表すことができるのに対して、より特殊化されたポインタ型は、特定の型のデータだけを指します。PByte 型は、文字データ以外の任意のバイト データに使用されます。ポインタは 4 バイトのメモリを占有します。

このトピックでは、以下について説明します。

  • ポインタ型の概要
  • Object Pascal でサポートされているポインタ型の宣言と使用

ポインタの概要

次の例は、ポインタの働きを示しています。

 1         var
 2           X, Y: Integer;  // X と Y は Integer 型の変数
 3           P: ^Integer     // P は Integer へのポインタ
 4           begin
 5             X := 17;      // X に値を代入する
 6             P := @X;      // X のアドレスを P に代入する
 7             Y := P^;      // P を逆参照し、その結果を Y に代入
 8           end;

2 行目では、XYInteger 型の変数として宣言します。3 行目では、PInteger 値へのポインタとして宣言します。つまり、PX または Y の場所を指すことができます。5 行目では X に値を代入し、6 行目では X のアドレス(@X と表す)を P に代入します。最後に 7 行目では、P の指す場所(アドレス)に格納されている値(P^ と表す)を取得して Y に代入します。このコードの実行後、XY の値はどちらも 17 になります。

ここで変数のアドレスを取得するのに使用した @ 演算子は、関数や手続きに対しても使用できます。詳細については、「@ 演算子」と「ステートメントと式の中の手続き型」を参照してください。

^ 記号には 2 つの用途があり、この例ではどちらの用途も示されています。1 つは、型識別子の前に現れる場合です。

^typeName

この場合は、指定された型 typeName の変数を指すポインタ型を表します。

もう 1 つは、ポインタ変数の後に現れる場合です。

pointer^

この場合はポインタを逆参照します。つまり、ポインタが指すメモリ アドレスに格納されている値を返します。

この例は、ある変数の値を別の変数にコピーする方法としては回りくどいように思われるかもしれません。単純な代入文でも同じことを実現できます。しかし、ポインタが役に立つ理由はいくつかあります。まず、ポインタを理解することで Object Pascal 言語の理解が深まります。コードに明示的にポインタが現れない場合でも、多くの場合、裏ではポインタが働いているからです。動的に割り当てられる大きいメモリ ブロックが必要なデータ型では、ポインタを使用します。たとえば、長い文字列を格納する変数は暗黙的にポインタであり、それはクラス インスタンス変数も同じです。さらに、一部の高度なプログラミング技法ではポインタを使用する必要があります。

最後に、ポインタ以外に Object Pascal の厳密な型指定を回避する方法がない場合もあります。汎用的な 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+} が有効な場合(デフォルト)は、ポインタの参照時にキャレットを省略できます。しかし、ポインタの宣言時や、ポインタが別のポインタを指すときにあいまいさを解消するには、やはりキャレットが必要です。詳細については、「拡張構文(Object Pascal)」を参照してください。

次の例に示すように、拡張構文が有効な場合は、ポインタの参照時にキャレットを省略できます。

{$X+}
 type
   PMyRec = ^TMyRec;
   TMyRec = record
     Data: Integer;
   end;

 var
   MyRec: PMyRec;

 begin
   New(MyRec);
   MyRec.Data := 42;  {#1}
 end.

拡張構文が無効な場合、通常 {#1} マークの付いた行は次のように表されます。

 MyRec^.Data := 42;

ポインタ型

次の構文を使用すると、どのような型へのポインタでも宣言できます。

type pointerTypeName = ^type

レコード型などのデータ型を定義するときは、その型へのポインタも定義しておくと便利なことがあります。このようなポインタ型を定義すると、大きいメモリ ブロックをコピーせずにその型のインスタンスを簡単に操作できるようになります。

メモ:  ポインタ型を宣言してから、そのポインタの指す型を宣言することができます。

さまざまな用途のために標準のポインタ型が用意されています。最も柔軟性が高いのは Pointer 型で、任意の型のデータを指すことができます。ただし、Pointer 型変数は逆参照できません。^ 記号を Pointer 型変数の後に付けると、コンパイル エラーになります。Pointer 型変数で参照されるデータにアクセスするには、まず別のポインタ型に型キャストしてから逆参照します。

文字ポインタ

基本型 PAnsiChar および PWideChar は、それぞれ AnsiChar 値および WideChar 値へのポインタを表します。汎用の PCharChar へのポインタ(つまり、現在の実装では WideChar へのポインタ)を表します。これらの文字ポインタは、Null で終わる文字列の操作に使用します(「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+} または {$T-}

{$T-} の状態では、@ 演算子の結果の型は常に、他のすべてのポインタ型と互換性のある型なしポインタになります。{$T+} の状態で @ が変数参照に適用される場合、適用結果の型は ^T になります。ここで、T は、その変数の型へのポインタとしか互換性がありません。

その他の標準ポインタ型

System ユニットと SysUtils ユニットには、よく使用される標準のポインタ型が多数宣言されています。

{POINTERMATH <ON|OFF>} 指令を使用すると、すべての型指定ポインタに対してポインタ算術演算を有効または無効にできます。有効な場合、インクリメントやデクリメントは要素のサイズ単位で行われます。

System と SysUtils に宣言されているポインタ型(抜粋):

ポインタ型 指す変数の型

PString

UnicodeString

PAnsiString

AnsiString

PByteArray

TByteArraySysUtils に宣言)。動的に割り当てられたメモリを型キャストして配列にアクセスするのに使用されます。

PCurrencyPDoublePExtendedPSingle

CurrencyDoubleExtendedSingle

PInteger

Integer

POleVariant

OleVariant

PShortString

ShortString 。以前の PString 型を使用する従来のコードを移植する際に役に立ちます。

PTextBuf

TTextBufSysUtils に宣言)。TTextBufTTextRec ファイル レコードで使用される内部バッファ型です。

PVarRec

TVarRecSystem に宣言)

PVariant

Variant

PWideString

WideString

PWordArray

TWordArraySysUtils に宣言)。動的に割り当てられたメモリを型キャストして 2 バイト値の配列にするために使用されます。

関連項目