1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

<10> ポインタ型 (標準 Pascal 範囲内での Delphi 入門)

10. ポインタ型 (Pointer Types)

これまでに説明されてきた型はいずれも静的に割り当てられる静的変数でした。動的変数 (対象変数)動的に作れる変数です。ポインタ型はプログラマ定義の型 1 です。

10.1. ポインタ変数と動的変数 (対象変数)

動的変数 (対象変数)そのものは変数宣言部に現れることはなく、名前がないため直接アクセスすることはできません。

その代わりに標準手続き New()Dispose() を使って生成と削除を行い、ポインタ値 (割り当てられた動的変数を指し示す識別値) によって識別を行います。このため、ポインタ値は "その型に適合するポインタ型 (型付きポインタ型) の変数" に代入する必要があります。

ポインタ型 = 
 ^ 型名.

例えば文字型 Char に対するポインタ型は次のように定義できます。

type
  TPChar = ^Char;

次のようにポインタ型 (文字ポインタ型) の定義と変数の宣言を同時に行うこともできます。

var
  P: ^Char;

See also:

(10.1.1.) ポインタ型の宣言

ポインタ型は Pascal の文法としては例外的に、ポインタ型を先に定義してから、そのポインタの指す型を定義する事ができます。例えばレコード型 TFoo とそのポインタ型 PFoo を定義するには次のようになりますが、

type
  TFoo = record
    v1: Integer;
    v2: Integer;
  end;
  PFoo = ^TFoo; { TFoo のポインタ型 PFoo を定義 }

ポインタ型 PFoo はレコード型 TFoo より前に定義する事もできます。

type
  PFoo = ^TFoo; { TFoo のポインタ型 PFoo を定義 }
  TFoo = record
    v1: Integer;
    v2: Integer;
  end;

10.2. New と Dispose

動的変数を作成してそのポインタ値を Ptr に代入するには New(Ptr) を使います。動的変数が不要になった場合には Dispose(Ptr) を使って動的変数が使用していた領域を解放します。この時、Ptr の値は不定となります。

手続き 説明
New() 新しい動的変数を作成し、パラメータに渡された
ポインタ変数を動的変数のポインタに設定する。
Dispose() 動的変数用に割り当てられたメモリを解放する。
PointerTest1.pas
program PointerTest1(output); 
type
  TPChar = ^Char;
var
  Ptr: TPChar;
  C: Char;
begin
  New(Ptr);     { 動的変数の領域を確保し、ポインタ値を Ptr に設定 }

  Ptr^ := 'A';
  C := Ptr^;

  Dispose(Ptr); { 動的変数の領域を解放 }
  Ptr := nil;

  Writeln(C);
end.

ポインタ型 Ptr があるとき、動的変数そのものは Ptr^ で表します。これはポインタの逆参照 (Dereference) となります。^逆参照演算子 (Dereference Operator) と呼ばれます。

ポインタ型には動的変数が割り当てられていないことを示す予約語 nil を代入しておく事ができます。nil は任意のポインタに代入できる特殊な定数です。 nil をポインタに代入すると、そのポインタは何も参照しません。

ポインタ型を作るときは型の前に ^、動的変数にアクセス (逆参照) する時にはポインタ変数の後に ^ を付けるのですが、同じ記号で別の意味を持つと紛らわしいですよね。

Pascal
type
  T: Integer;
  P = ^T;

See also:

(10.2.1.) New と Dispose の二番目の書式

標準 Pascal の New()Dispose() には、レコード型のための 可変個のパラメータを持つ二番目の書式 がありますが、Delphi ではサポートされていません。

手続き 説明
New(P, C1,..., Cn) 新しい動的変数を作成し、パラメータに渡された
ポインタ変数を動的変数のポインタに設定する。C1..Cn で入れ子になったレコード型の可変部を指定する。
Dispose(Q, K1,..., Km) 動的変数用に割り当てられたメモリを解放する。K1..Km で入れ子になったレコード型の可変部を指定する。

(10.3.) @ 演算子

Delphi には @ 演算子があります。これは変数のポインタ値を参照する演算子です。変数 T のポインタ値は @T で参照できます。

PointerTest2.pas
program PointerTest2(output); 
type
  TPChar = ^Char;
var
  Ptr: TPChar;
  C: Char;
begin
  C := 'A';     // 変数 C に 'A' を代入
  Ptr := @C;    // 変数 C を指すポインタを Ptr に代入
  Ptr^ := 'B';  // Ptr を逆参照した先 (C) に 'B' を代入
  Writeln(C);   // 'B' が表示される
end.

See also:

(10.4.) ポインタ定数とグローバルポインタ変数の初期化

Delphi では次のようなポインタ定数が使えます。

PointerTypeConstantsTest.pas
program PointerTypeConstantsTest;
{$APPTYPE CONSOLE}

// グローバル変数
var
  I: Integer = 0;

// グローバルポインタ定数
const
  PInt1: ^Integer = @I;

// グローバルポインタ変数と初期化
var
  PInt2: ^Integer = @I;

  procedure Sub;
//  ローカル配列変数は初期化できない
//var
//  PInt3: ^Integer = @I;
  begin
    ...
  end; { Sub }
begin
  ...
end.

使い所が難しいと思います。

See also:

(10.5.) Pointer 型

Delphi には型情報のないポインタ型 (void ポインタ) である Pointer という宣言済みのポインタ型があります。このポインタ型は型情報が関連付けられていないため逆参照できません。

PointerTest4.pas
program PointerTest4(output);
type
  TPChar = ^Char;
var
  P: Pointer;
  C: Char;
begin
  New(TPChar(P));     { 動的変数の領域を確保し、ポインタ値を P に設定 }

//P^ := 'A';         // NG
  TPChar(P)^ := 'A'; // OK
  C := TPChar(P)^;

  Dispose(TPChar(P)); { 動的変数の領域を解放 }
  P := nil;

  Writeln(C);
end.

型付きのポインタ型にキャストして逆参照する事は可能です。

See also:

(10.6.) 型チェック済みポインタ

@ 演算子が返すポインタ型は {$T} コンパイラ指令によって変化します。デフォルトの {$T-} の状態では @ 演算子の結果の型は常に他のすべてのポインタ型と互換性のある型なしポインタ (Pointer 型) になります。{$T+} の状態で @ が変数参照に適用される場合、変数 T に適用された結果の型は ^T になります。ここで、T は、その変数の型へのポインタとしか互換性がありません。

var
  PI: PInteger;
  PD: ^Double;
  I: Integer;
begin
  PI := @I;
  PD := @I; // {$T+} だとコンパイルエラーになる
end.

See also:

(10.7.) 型キャスト

先述のように、Delphi では型キャストを行えます。型キャストとはある式を別の型の式であるかのように扱えるようにする機能です。

See also:

(10.7.1.) 値 型キャスト

値型キャストでは、型識別子とキャスト式の両方が、順序型またはポインタ型でなければなりません。

Integer('A')
Char(48)
Boolean(0)
Integer(@Buffer)

(10.7.2.) 変数 型キャスト

変数は、サイズが同じであり、整数と実数を混在させない限り、任意の型にキャストすることができます。

Char(I)
Boolean(Count)
TSomeDefinedType(MyVariable)

(10.8.) ポインタ演算 (Pointer Arithmetic)

Delphi では 2009 以降で、型付きポインタ (Pointer 型以外) に対してポインタ演算を行う事ができるようになっていますが、デフォルトではオフになっています。

ポインタ演算を行えるようにするには {$POINTERMATH ON} コンパイラ指令を記述します。

See also:

索引

[ ← 9. ファイル型 ] [ ↑ 目次へ ] [ → 11. 手続きと関数 ] :sushi:


  1. プログラマ定義の型 は ISO/IEC 7185 の構文定義では new-type、JIS X 3008 では 書下(くだ)し型 と記述されています。『J&W』の構文定義には記述がありません。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
1
Help us understand the problem. What are the problem?