単純型(Delphi)

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

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

単純型には順序型と実数型があり、順序付けされた値の集合を定義します。

順序型

順序型には、整数型、文字型、論理型、列挙型、および部分範囲型が含まれます。順序型は、順序付けされた値の集合を定義します。この集合では、値(最初の値を除きすべて)の前の値と、値(最後の値を除きすべて)の後の値が、それぞれ一意に決まっています。さらに、すべての値が順序値を持ち、これによって型の順序付けが決定されます。ほとんどの場合、値の順序値が n であれば、前の値の順序値は n-1 であり、後の値の順序値は n+1 です。

整数型の場合、順序値は値そのものです。部分範囲型はその基底型の順序値を保持します。その他の順序型では、デフォルトで最初の値の順序値が 0、次の値の順序値が 1 というように決まっています。列挙型を宣言すると、このデフォルト値を明示的にオーバーライドできます。

順序型の値と型識別子を処理する複数の定義済み関数があります。特に重要なものを以下に示します。

関数 パラメータ 戻り値 備考

Ord

順序型の式

式の値の順序値

Int64 の引数をとらない

Pred

順序型の式

式の値の前の値

Succ

順序型の式

式の値の次の値

High

順序型の型識別子または順序型の変数

型の最大の値

短い文字列型と配列型も処理できる

Low

順序型の型識別子または順序型の変数

型の最小の値

短い文字列型と配列型も処理できる


たとえば、Byte 型の最大の値は 255 なので、High(Byte) は 255 を返します。2 の次の値は 3 なので、Succ(2) は 3 を返します。

標準手続き IncDec は順序型変数の値をインクリメントおよびデクリメントします。たとえば、Inc(I)I := Succ(I) と同じであり、I が整数型変数である場合は I := I + 1 と同じです。

整数型

整数型は汎整数の部分集合を表します。

整数型には、プラットフォーム依存のものとプラットフォーム非依存のものがあります。

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

プラットフォーム依存の整数型は、現在のコンパイラ プラットフォームのビット サイズに合わせて変換されます。プラットフォーム依存の整数型は NativeIntNativeUIntLongIntLongWord です。結果的に、ベースとなる CPU およびオペレーティング システムの最高のパフォーマンスが得られるため、できるだけこれらの型を使用します。以下の表では、これらの型の値の範囲と格納形式を Delphi コンパイラの場合について示します。

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

値の範囲 形式 エイリアス

NativeInt

-2147483648..2147483647
-2^63..2^63-1

符号付き 32 ビット(32 ビット プラットフォーム上)または
符号付き 64 ビット(64 ビット プラットフォーム上)

Integer
Int64

NativeUInt

0..4294967295
0..2^64-1

符号なし 32 ビット(32 ビット プラットフォーム上)または
符号なし 64 ビット(64 ビット プラットフォーム上)

Cardinal
UInt64

LongInt

-2147483648..2147483647
-263..263-1

32 ビット プラットフォームおよび 64 ビット Windows プラットフォーム
64 ビット iOS プラットフォーム

Integer
Int64

LongWord

0..4294967295
0..264-1

32 ビット プラットフォームおよび 64 ビット Windows プラットフォーム
64 ビット iOS プラットフォーム

Cardinal
UInt64

メモ: 32 ビット プラットフォームには、32 ビット Windows、OSX32、32 ビット iOS、Android が含まれます。

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

プラットフォーム非依存の整数型は、使用するプラットフォームにかかわらず、常に同じサイズです。プラットフォーム非依存の整数型には、ShortIntSmallIntLongIntIntegerInt64ByteWordLongWordCardinalUInt64 があります。

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

値の範囲 形式 エイリアス

ShortInt

-128..127

符号付き 8 ビット

Int8

SmallInt

-32768..32767

符号付き 16 ビット

Int16

FixedInt

-2147483648..2147483647

符号付き 32 ビット

Int32

Integer

-2147483648..2147483647

符号付き 32 ビット

Int32

Int64

-2^63..2^63-1

符号付き 64 ビット

Byte

0..255

符号なし 8 ビット

UInt8

Word

0..65535

符号なし 16 ビット

UInt16

FixedUInt

0..4294967295

符号なし 32 ビット

UInt32

Cardinal

0..4294967295

符号なし 32 ビット

UInt32

UInt64

0..2^64-1

符号なし 64 ビット


一般に、整数に対する算術演算は Integer 型の値を返します。これは、32 ビットの LongInt と同じです。演算が Int64 型の値を返すのは、1 つまたは複数の Int64 オペランドを実行した場合だけです。したがって、次のコードは不正な結果を返します。

var
I: Integer;
J: Int64;
... 
I := High(Integer);
J := I + 1;

この状況で Int64 の戻り値を取得するには、IInt64 に型キャストします。

...
J := Int64(I) + 1;

詳細は、「算術演算子」を参照してください。

メモ: 整数引数をとる標準ルーチンには、Int64 を 32 ビットに切り捨てるものがあります。ただし、HighLowSuccPredIncDecIntToStr、および IntToHex ルーチンは、Int64 引数を完全にサポートします。また、RoundTruncStrToInt64、および StrToInt64Def 関数は、Int64 値を返します。一部のルーチンは Int64 の値をまったく受け取ることができません。

整数型の最後の値をインクリメントすると範囲の最初の値に、最初の値をデクリメントすると最後の値に戻ります。たとえば、ShortInt 型の範囲は -128~127 です。したがって、次のコードを実行すると、

var
I: Shortint;
...
I := High(Shortint);
I := I + 1;

I の値は -128 になります。ただし、コンパイラによる範囲検査を有効にしている場合は、このコードを実行すると実行時エラーが発生します。

文字型

文字型には、CharAnsiCharWideCharUCS2CharUCS4Char があります。

  • 現在の実装では、デフォルトの文字列型が UnicodeString であるため、CharWideChar と同等です。Char の実装は今後のリリースで変更されることがあるため、サイズの異なる文字を処理しなければならない可能性のあるプログラムを作成するときは、定数をハードコードするのではなく、標準関数 SizeOf を使用した方がよいでしょう。
  • AnsiChar の値はサイズが 1 バイト(8 ビット)の文字であり、ロケール文字セット(マルチバイトの場合もある)に従って順序付けされています。
  • WideChar 文字は、複数のバイトを使用してすべての文字を表します。現在の実装では、WideChar はサイズが 1 ワード(16 ビット)の文字であり、Unicode 文字セットに従って順序付けされています(将来の実装では、サイズがさらに大きくなる可能性があります)。Unicode の先頭から 256 文字が ANSI 文字に対応します。
  • UCS2Char は、WideChar のエイリアスです。
  • UCS4Char は、4 バイトの Unicode 文字を処理する場合に使用します。

長さが 1 の文字列定数("A" など)は、文字値を表すことができます。定義済み関数 Chr は、WideChar(たとえば、Chr(65) は文字 A を返す)の範囲の整数に対応する文字値を返します。

整数と同様に、AnsiCharWideChar の値は範囲の先頭をデクリメントすると範囲の末尾に、末尾をインクリメントすると先頭に戻ります(範囲検査を有効にしていない場合)。たとえば、次のコードを実行したとします。

var Letter: AnsiChar;
I: Integer;
begin
  Letter := High(Letter);
  for I := 1 to 66 do Inc(Letter);
end;

実行後の Letter の値は A(ASCII 65)です。

メモ: AnsiChar 型は、Delphi モバイル コンパイラではサポートされていませんが、Delphi デスクトップ コンパイラでは使用されています。詳細については、「デスクトップからモバイルへの Delphi コードの移行」を参照してください。

論理型

4 つの定義済みの論理型として、BooleanByteBoolWordBoolLongBool があります。Boolean が通常使用される型です。他の 3 つの型は、他の言語やオペレーティング システムのライブラリとの互換性を提供するために用意されています。

変数のサイズは、Boolean 変数と ByteBool 変数が 1 バイト、WordBool 変数が 2 バイト(1 ワード)、LongBool 変数が 4 バイト(2 ワード)です。

論理値は、定義済みの定数 True および False で表されます。次の関係が成立します。

Boolean ByteBool、WordBool、LongBool

False < True

False <> True

Ord(False) = 0

Ord(False) = 0

Ord(True) = 1

Ord(True) <> 0

Succ(False) = True

Succ(False) = True

Pred(True) = False

Pred(False) = True


ByteBool 型、LongBool 型、または WordBool 型の値は、順序値がゼロ以外の場合、True とみなされます。Boolean が予期される状況でこれらの値が使用された場合、コンパイラは順序値がゼロ以外の値を自動的に True に変換します。

なお、これは論理値の順序値に関する説明であり、論理値そのものに関するものではありません。Delphi では論理型の式を整数や実数と等価にみなすことはできません。X が整数変数であるときに、次のステートメントを記述したとします。

if X then ...;

これは、コンパイル エラーになります。この変数を論理型に型キャストしても結果は保証されませんが、以下の方法を使用すると問題を解決できます。

 if X <> 0 then ...;    { use an expression that returns a Boolean value }
  ...
 var OK: Boolean;       { use a Boolean variable }
   ... 
 if X <> 0 then
   OK := True;
 if OK then ...;

列挙型

列挙型では、値を表す識別子を列挙して、順序付けされた値の集合を定義します。値そのものが意味を持つものではありません。列挙型を宣言するには、次の構文を使用します。

 type typeName = (val1, ...,valn)

typeNameval には有効な識別子を指定します。たとえば、次の宣言があるとします。

type Suit = (Club, Diamond, Heart, Spade);

これは、Suit という列挙型を定義しています。有効な値は、ClubDiamondHeart、および Spade です。Ord(Club) は 0 を Ord(Diamond) は 1 を返します。

列挙型を宣言すると、それぞれの valtypeName という型の定数であることが宣言されます。val の識別子が同じスコープ内で別の目的に使用されている場合は、名前の競合が発生します。たとえば、次の型を宣言したとします。

type TSound = (Click, Clack, Clock)

残念ながら、Click は TControl と VCL 内のすべての下位オブジェクトで定義されているメソッドの名前でもあります。アプリケーションを作成するときに次のようなイベント ハンドラを書いたとします。

 procedure TForm1.DBGridEnter(Sender: TObject);
  var
     Thing: TSound;
     begin 
       ... 
       Thing := Click;
     end;

これは、コンパイル エラーになります。コンパイラは、手続きのスコープ内の Click を TForm の Click メソッドへの参照と解釈します。これは、識別子を修飾することによって対処できます。たとえば、TSoundMyUnit の中で宣言されている場合は、次のステートメントを使用します。

Thing := MyUnit.Click;

ただし、他の識別子と競合する可能性が少ない定数名を選択する方が、解決策としては優れています。以下に例を示します。

type
  TSound = (tsClick, tsClack, tsClock);
  TMyColor = (mcRed, mcBlue, mcGreen, mcYellow, mcOrange);
  Answer = (ansYes, ansNo, ansMaybe)

(val1, ..., valn) という構文は、型の名前と同じように変数宣言で直接使用できます。

var MyCard: (Club, Diamond, Heart, Spade);

しかし、この方法で MyCard を宣言すると、同じ定数識別子を使って同一スコープ内で別の変数を宣言できません。次のコードがあるとします。

 var Card1: (Club, Diamond, Heart, Spade);
 var Card2: (Club, Diamond, Heart, Spade);

これは、コンパイル エラーになります。しかし、

var Card1, Card2: (Club, Diamond, Heart, Spade);

これは正しくコンパイルされます。次も同様です。

type 
  Suit = (Club, Diamond, Heart, Spade); 
  var 
    Card1: Suit;
    Card2: Suit;

明示的に順序値を割り当てられた列挙型

デフォルトでは、列挙型の値の順序値は 0 から始まり、型宣言で識別子を記述したときと同じ順序になります。これを明示的にオーバーライドするには、宣言において一部またはすべての値に順序値を割り当てます。順序値を割り当てるには、その識別子の後に = constantExpression を記述します。ここで、constantExpression は、整数と評価される定数式です。以下に例を示します。

type Size = (Small = 5, Medium = 10, Large = Small + Medium);

これは、Size という型を定義します。有効な値は、SmallMedium、および Large です。Ord(Small) は 5、Ord(Medium) は 10、Ord(Large) は 15 を返します。

列挙型は本質的には部分範囲型であり、その上限と下限の値は、宣言における定数の順序値の上限と下限の値によって決まります。上の例で Size 型が取ることのできる値は 11 個で、順序値の範囲は 5 ~ 15 です。したがって、array[Size] of Char 型は、11 文字の配列を表します。上記の例では、3 つの値にのみ名前があります。ただし、他の値は、型キャストや PredSuccIncDec などのルーチンを通してアクセス可能です。次の例では、Size の範囲内の "無名" 値が変数 X に割り当てられています。

var 
  X: Size; 
  X := Small;   // Ord(X) = 5 
  X := Size(6); // Ord(X) = 6 
  Inc(X);       // Ord(X) = 7

順序値が明示的に割り当てられていない値は、リストの中の直前の値の順序値より 1 つ大きい値になります。最初の値に順序値が割り当てられていない場合は、その順序値は 0 になります。したがって、次のように宣言した場合、

type SomeEnum = (e1, e2, e3 = 1);

SomeEnum の有効な値は 2 つだけです。Ord(e1) は 0 を返し、Ord(e2) は 1 を返し、Ord(e3) も 1 を返します。e2e3 は同じ順序値で、同じ値を表します。

列挙型定数で値を指定しない場合、実行時型の情報をとります。

type SomeEnum = (e1, e2, e3);

それに対して、以下のように列挙型定数で値を指定する場合、実行時型の情報はとりません。

type SomeEnum = (e1 = 1, e2 = 2, e3 = 3);

スコープのある列挙型

{$SCOPEDENUMS ON} コンパイラ指令を有効した場合は、スコープのある列挙型を Delphi コードで使用できます。

{$SCOPEDENUMS ON または OFF} コンパイラ指令を指定することにより、スコープのある列挙型を Delphi コードで使用できる(または使用できない)ようになります。{$SCOPEDENUMS ON} は、列挙型がスコープ付きであることを明示します。{$SCOPEDENUMS ON} は、最も近い {$SCOPEDENUMS OFF} 指令まで列挙型の宣言に作用します。{$SCOPEDENUMS ON} 指令の後で宣言された列挙型のメンバである識別子はグローバル スコープには追加されません。スコープのある列挙型の識別子を使用するには、その識別子が含まれている列挙型の名前で識別子を修飾しなければなりません。

たとえば、Unit1.pas ファイルに次のようなユニットを定義しましょう。

unit Unit1;
interface
// {$SCOPEDENUMS ON} // clear comment from this directive
 type
   TMyEnum = (First, Second, Third);
implementation
 
end.

そして、このユニットを使用する次のようなプログラムを定義しましょう。

program Project1;
{$APPTYPE CONSOLE}

uses
  SysUtils, Unit1 in 'Unit1.pas';
 
var
  // First: Integer;  // clear comment from this variable
  Value: TMyEnum;
begin
  try
    Value := First;   
//  Value := TMyEnum.First; 
//  Value := unit1.First; 
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

さてこれで、TMyEnum 列挙型に定義されている FirstSecondThird の各識別子を参照できるスコープが {$SCOPEDENUMS} コンパイラ指令でどのように影響されるかを調べることができます。

まず、このコードに対して、メイン メニューから[実行|実行](F9)を選択します。コードは正常に動作します。つまり、次の変数で使用されている First 識別子は、

Value := First;

次の列挙型のメンバになっているグローバル スコープ識別子である

TMyEnum = (First, Second, Third);

ということです。

では、次のコンパイラ指令からコメントを取り除いてみましょう。

{$SCOPEDENUMS ON}

この指令は unit1 ユニットに含まれています。この指令により、TMyEnum 列挙型はスコープのある列挙型になります。メイン メニューから[実行|実行]を選択します。"E2003 宣言されていない識別子: 'First'" というエラーが

Value := First;

上記の行で発生します。このエラーは、{$SCOPEDENUMS ON} コンパイラ指令があるために、(スコープのある列挙型 TMyEnum に含まれている)First 識別子がグローバル スコープに追加されないことを示しています。

スコープのある列挙型のメンバである識別子を使用するには、その列挙型の名前でメンバ名を修飾します。たとえば、次の行のコメントを取り除きます。

Value := TMyEnum.First;

これは Value 変数の第 2 バージョンです(なお、Value の第 1 バージョンはコメントにしておきます)。メイン メニューから[実行|実行]を選択します。プログラムは正常に動作します。つまり、First 識別子は TMyEnum スコープで既知であるということです。

さて、次のようにコンパイラ指令をコメントにします。

// {$SCOPEDENUMS ON}

この指令は unit1 に含まれています。その後で、First 変数の宣言からコメントを取り除いて次のようにしたうえで、

First: Integer; 

再度

Value := First;

上記の変数を使用します。これで、program Project1 のコードは次のようになります。

var
  First: Integer;   
  Value: TMyEnum;
begin
  try
    Value := First;

メイン メニューから[実行|実行]を選択します。下記の行

 First: Integer;   

が原因となって、"E2010 'TMyEnum' と 'Integer' には互換性がありません" というエラーが発生します。これは、列挙型 TMyEnum のメンバであるグローバル スコープの First 識別子と First 変数の間で名前の競合が発生しているということです。First 識別子をその定義元である unit1 ユニットで修飾することで、この競合を回避することができます。それを確かめるため、Value 変数の第 1 バージョンを再度コメントにしたうえで、下記の第 3 バージョンからコメントを取り除きます。

Value := unit1.First;

メイン メニューから[実行|実行]を選択します。プログラムは正常に動作します。つまり、First 識別子を unit1 ユニット スコープで修飾できるわけです。しかし、ここで次のコンパイラ指令を再度有効にしたら、どうなるでしょうか。

{$SCOPEDENUMS ON}

この指令は unit1 に含まれています。"E2003 宣言されていない識別子: 'First'" というコンパイラ エラーが

Value := unit1.First;

上記の行で発生します。これは、{$SCOPEDENUMS ON} があるために、列挙型のメンバである First 識別子が unit1 スコープに追加されないということです。First 識別子は TMyEnum 列挙型のスコープにのみ追加されます。これを確かめるため、次の行を再度使用しましょう。

Value := TMyEnum.First;

これは Value 変数の第 2 バージョンです。メイン メニューから[実行|実行]を選択すると、コードは正常に動作します。

部分範囲型

部分範囲型は、別の順序型の値の部分集合を表します。部分集合の基となる順序型は基底型と呼ばれます。LowHigh が同じ順序型の定数式であり、LowHigh より小さい場合、Low..High という形式の構文は Low から High までのすべての値を含む部分範囲型を表します。たとえば、次の列挙型を宣言したとします。

type 
  TColors = (Red, Blue, Green, Yellow, Orange, Purple, White, Black);

この場合は、次のような部分範囲型を定義できます。

type 
  TMyColors = Green..White;

ここで、TMyColors の値は、GreenYellowOrangePurple、および White です。

数値定数と文字(長さが 1 の文字列定数)を使って部分範囲型を定義することもできます。

type 
 SomeNumbers = -128..127;
 Caps = 'A'..'Z';

数値定数や文字定数を使って部分範囲型を定義するときは、指定した範囲を含む最小の整数型または文字型が基底型になります。

LowerBound..UpperBound という構文はそれ自体が型の名前として機能するので、変数の宣言で直接使用できます。たとえば、次のとおりです。

var SomeNum: 1..500;

これは 1 ~ 500 の範囲の任意の値をとる整数変数を宣言します。

部分範囲型の各値の順序値には、基底型における順序値が使用されます。最初の例では、変数 ColorGreen という値が格納されている場合、Color の型が TColors であるか TMyColors であるかに関係なく、Ord(Color) は 2 を返します。部分範囲型の先頭または末尾を超えてインクリメントまたはデクリメントを実行した場合は、基底型が整数型または文字型の場合でも、値が部分範囲の末尾または先頭に戻ることはありません。値の型が部分範囲型から基底型に変換されるだけです。次のコードがあるとします。

 type Percentile = 0..99;
 var I: Percentile;
   ... 
   I := 100;

ここではエラーが発生します。

 ...
 I := 99;
 Inc(I);

これは、値 100 を I に代入します(コンパイラの範囲検査が有効ではない場合)。

部分範囲の定義で定数式を使用すると、構文上の問題が発生する場合があります。型宣言で = の次の文字(意味を持つ)が左かっこである場合、コンパイラは列挙型が定義されているものとみなします。次のコードがあるとします。

 const X = 50; Y = 10;
 type Scale = (X - Y) * 2..(X + Y) * 2;

ここではエラーが発生します。この問題を回避するには、次のとおり先頭がかっこにならないように型宣言を書き換えます。

 type Scale = 2 * (X - Y)..(X + Y) * 2;

実数型

実数型は、浮動小数点表記で表すことができる数の集合を定義します。以下の表では、64 ビット プラットフォームと 32 ビット プラットフォームにおける実数型の値の範囲と格納形式を示します。

実数型

正の値の範囲(概数) 有効桁数 サイズ(バイト数)
Real48 2.9e-39 .. 1.7e+38 11-12 6
Single 1.5e-45 .. 3.4e+38 7-8 4
Double 5.0e-324 .. 1.7e+308 15-16 8
Real 5.0e-324 .. 1.7e+308 15-16 8
Extended
  • 32 ビット プラットフォーム: 3.4e-4932 .. 1.1e+4932
  • 64 ビット プラットフォーム: 5.0e-324 .. 1.7e+308
10-20

15-16

10

8

Comp -263+1 .. 263-1 10-20 8
Currency -922337203685477.5808.. 922337203685477.5807 10-20 8

以下の備考は実数型に関するものです。

  • Real は、現在の実装では、Double と同等です。
  • Real48 は下位互換性のためにサポートされています。この型の格納形式は Intel プロセッサ アーキテクチャにネイティブではないため、結果的に、他の浮動小数点型よりもパフォーマンスが低くなります。
以前のバージョンの Object Pascal では、6 バイトの Real48 型が Real と呼ばれていました。Delphi の 6 バイト Real 型を使用するコードを再コンパイルする場合は、Real48 に変換することをお勧めします。また、{$REALCOMPATIBILITY ON} コンパイラ指令を使って Real を 6 バイトの型に戻すこともできます。
  • Extended は、32 ビット プラットフォームでは、他の実数型よりも精度が高くなります。
64 ビット プラットフォームでは、ExtendedDouble のエイリアスです。つまり、Extended データ型のサイズは 8 バイトになります。したがって、64 ビット プラットフォームで Extended を使用すると、32 ビット プラットフォームの場合(Extended のサイズは 10 バイト)と比べて、精度が低くなります。そのため、アプリケーションで Extended データ型を使用していて、浮動小数点演算の精度に頼っている場合は、サイズのこのような違いがデータに影響を及ぼすおそれがあります。クロス プラットフォームで共有するデータ ファイルを作成する場合は、Extended の使用に注意してください。詳細は、「64 ビット Windows システムでは Extended データ型のサイズは 2 バイト小さい」を参照してください。
  • Comp(計算)型は Intel プロセッサ アーキテクチャに固有のもので、64 ビット整数を表します。しかし、この型は順序型と動作が異なるので、実数型に分類されています たとえば、Comp 型の値をインクリメントまたはデクリメントできません。Comp は下位互換性のためにのみ維持されています。Int64 型を使用する方が、パフォーマンスが優れています。
  • Currency 型は、金額計算での丸め誤差が非常に少ない固定小数点データ型です。これは最下位 4 桁が暗黙に小数点以下の桁を表す位取り 64 ビット整数として格納されます。代入文や式で他の実数型と混在させた場合は、Currency 型の値に対して自動的に 10,000 での除算または乗算が行われます。

関連項目