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

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


## 10.1. ポインタ変数 (Pointer Variables) と動的変数 (対象変数) (Dynamic (Identified) Variables)
**動的変数 (対象変数)** そのものは[変数宣言部](https://qiita.com/ht_deko/items/2d5996cbfc82d734b247#35-%E5%A4%89%E6%95%B0%E5%AE%A3%E8%A8%80%E9%83%A8)に現れることはなく、名前がないため直接アクセスすることはできません。

その代わりに[標準手続き](https://qiita.com/ht_deko/items/15b27e00fac9d723d9f0#%E6%A8%99%E6%BA%96%E6%89%8B%E7%B6%9A%E3%81%8D-required-procedures) **New()** と **Dispose()** を使って生成と削除を行い、ポインタ値 (割り当てられた動的変数を指し示す識別値) によって識別を行います。このため、ポインタ値は "その型に適合するポインタ型 (型付きポインタ型) の変数" に代入する必要があります。

```
ポインタ型 = 
　^ 型名.
```

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

```pascal
type
  TPChar = ^Char;
```

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

```pascal
var
  P: ^Char;
```

**See also:**

 - [ポインタとポインタ型（Delphi）(DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E3%81%A8%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%9E%8B%EF%BC%88Delphi%EF%BC%89)


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

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

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

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

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

|手続き|説明|
|:---|:---|
|[New()](http://docwiki.embarcadero.com/Libraries/ja/System.New)|新しい動的変数を作成し、パラメータに渡された<br>ポインタ変数を動的変数のポインタに設定する。|
|[Dispose()](http://docwiki.embarcadero.com/Libraries/ja/System.Dispose)|動的変数用に割り当てられたメモリを解放する。|


```pascal: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:Pascal
type
  T: Integer;
  P = ^T;
```

**See also:**

 - [【Delphi】String 型変数で身に覚えのないメモリリークが報告される (Qiita)](https://qiita.com/ht_deko/items/c3912518bb9c4e18b40a)

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

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

|手続き|説明|
|:---|:---|
|New(P, C<sub>1</sub>,..., C<sub>n</sub>)|新しい動的変数を作成し、パラメータに渡された<br>ポインタ変数を動的変数のポインタに設定する。C<sub>1</sub>..C<sub>n</sub> で入れ子になったレコード型の可変部を指定する。|
|Dispose(Q, K<sub>1</sub>,..., K<sub>m</sub>)|動的変数用に割り当てられたメモリを解放する。K<sub>1</sub>..K<sub>m</sub> で入れ子になったレコード型の可変部を指定する。|

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

```pascal: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:**

 - [@ 演算子 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%BC%8F%EF%BC%88Delphi%EF%BC%89#.40_.E6.BC.94.E7.AE.97.E5.AD.90)


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

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

```pascal: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:**

 - [ポインタ定数 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/Rio/ja/%E5%AE%A3%E8%A8%80%E3%81%95%E3%82%8C%E3%81%9F%E5%AE%9A%E6%95%B0#.E3.83.9D.E3.82.A4.E3.83.B3.E3.82.BF.E5.AE.9A.E6.95.B0
)



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

```pascal: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:**

 - [System.Pointer (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.Pointer)


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

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

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

**See also:**

 - [ポインタ型のチェック（Delphi）(DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%9E%8B%E3%81%AE%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%EF%BC%88Delphi%EF%BC%89)
 - [型チェック済みポインタ (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E3%81%A8%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%9E%8B%EF%BC%88Delphi%EF%BC%89#.E5.9E.8B.E3.83.81.E3.82.A7.E3.83.83.E3.82.AF.E6.B8.88.E3.81.BF.E3.83.9D.E3.82.A4.E3.83.B3.E3.82.BF)

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

**See also:**

 - [型キャスト (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%BC%8F%EF%BC%88Delphi%EF%BC%89#.E5.9E.8B.E3.82.AD.E3.83.A3.E3.82.B9.E3.83.88)

### (10.7.1.) 値 型キャスト

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

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


### (10.7.2.) 変数 型キャスト

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

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

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

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

**See also:**

 - [ポインタ演算 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/Pointer_Math_%EF%BC%88Delphi%EF%BC%89)
 - [32 ビット Delphi アプリケーションの 64 ビット Windows への変換 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/32_%E3%83%93%E3%83%83%E3%83%88_Delphi_%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE_64_%E3%83%93%E3%83%83%E3%83%88_Windows_%E3%81%B8%E3%81%AE%E5%A4%89%E6%8F%9B)

# 索引
[ [← 9. ファイル型](https://qiita.com/ht_deko/items/986fa01c9e6cfc3fd673) ] [ [↑ 目次へ](https://qiita.com/ht_deko/items/e2796788c65c2cda1de5) ] [ [→ 11. 手続きと関数](https://qiita.com/ht_deko/items/b93ac03bfee002f17137) ] [:sushi:](https://qiita.com/ht_deko/items/3be4294e82653fb234c4)

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