定数、型、変数の宣言は、プログラム,ユニット,手続き/関数などの宣言部で行います。
ここでは、基本的な宣言について説明します。
定数の宣言 |
|---|
定数の宣言は、const キーワードの後に、識別子に "=" を使用して値を設定します。設定した値の代わりに識別子を使用することができます。
定数には通常の定数と型付の定数があります。
値はリテラルや既知の定数だけではなくそれらを組み合わせた式を使用することができますが、コンパイル時にコンパイラが評価できるものに限られます。
通常の定数は、一度値を設定すると変更することはできません。
型付き定数の場合、既定では実行時に別の値を代入することにより定数設定値を変えることができます。これは過去の Turbo Pascal時代に変数宣言で初期化ができなかったため、型付き定数宣言でその機能を実装したためです。現在では変数宣言で初期化の設定が可能なため型付き定数は安全のために代入不可能な定数として扱うようにします。そのため、「{$J-}」ディレクティブを設定します。
特別な理由がなければ「{$J-}」ディレクティブは必ず設定するようにしましょう。
通常の定数 |
|---|
通常の定数とは、型を明示的に指定しない定数です。リテラル(直値)に名前がついたイメージです。明示的に指定しないだけで、コンパイル時にコンパイラが型を設定します。リテラルも同じです。
【通常の定数宣言】
const
識別子1 = 定数式1;
識別子2 = 定数式2;
:
識別子n = 定数式n;
識別子1〜n が定数です。定数式1〜n が各定数が表す値となります。式の結果の値です。
キーワード const で複数の定数の宣言ができます。それぞれ";"で終わります。
宣言キーワードまたは実行文部に会うと本宣言は終了します。
【通常の定数宣言の例】
const
COUNT = 10; // 10
MAX = COUNT * 10; // 100
NUM = Succ(COUNT); // 11
MARKA = 'A'; // 'A'
MARKB = Chr(66); // 'B'
VALUE = 1 + 1.0; // 2.0
IMAX = MaxInt; // 2147483647 MaxIntは Systemで定義されている定数
TWOPI = Pi * 2.0E3; // 6283.18530717958 Piは Systemで用意されている関数
AT0 = ArcTan(0); // 0.0 ArcTanは Systemで用意されている関数
定数式には、+, -, *, /, not, and, or, div, mod, ord, chr, sizeof, int, trunc, round, frac, odd などの演算子を使用することができます。
Pi や ArcTan のように Systemユニットで定義されている関数には定数式に使用できるものがあります。
Systemユニットは自動的に組み込まれるため uses句では指定しません。指定すると Systemユニットの重複エラーとなります。
【Systemや他のユニットとかぶった定数宣言の例】
const
PI = 3.14; // 3.14 System.Pi(3.14159265358979)を隠す。
MAXINT = 1000; // 1000 ObjPas.MaxInt(2147483647)を隠す。
円周率πの値は System.Pi で宣言されていますが、上記の PI が System.Pi を隠してしまいます。
Object Pascal(Free Pascal OBJFPCモード)では、MaxInt は ObjPas.MaxInt で宣言されていますが、上記の MAXINT が ObjPas.MaxInt を隠してしまいます。
System.MaxInt も宣言されていますが、ObjPas ユニットもSystemユニットの後に自動的に組み込まれるため、ObjPas.MaxINt が System.MaxInt を隠しています。
Object Pascalでは大文字と小文字の区別はないので MAXINT は MaxInt を隠します。
宣言がかぶって隠された場合は、ネームスペース(ユニット名)で修飾することにより参照できます。
PI ⇒ 3.14
System.Pi ⇒ 3.14159265358979
MAXINT ⇒ 1000
ObjPas.MaxInt ⇒ 2147483647
SYSTEM.MaxInt ⇒ 32767
【正しくない定数宣言の例】
const
VALUE = ArcCon(-1); // ArcCos は Systemの関数ではないのでコンパイル時に評価できない
NA = 3 div 0; // コンパイル時の評価で例外(Divide by zero)が発生する
ArcCos は Systemユニットではなく Mathユニットで定義されている Math.ArcCos関数です。実行時には使用できますがコンパイル時には評価できないので定数式に使用することはできません。
型付の定数 |
|---|
型付の定数とは、型を明示的に指定した定数です。通常の定数と同じですが、型の決定をコンパイラに任せず自分で指定するものです。
【型付の定数宣言】
const
識別子1: 型1 = 定数式1;
識別子2: 型2 = 定数式2;
:
識別子n: 型n = 定数式n;
識別子1〜n が定数です。型1〜n は各定数の型です。定数式1〜n が各定数が表す値となります。式の結果が指定の型に変換されて値となります。
通常の定数宣言の識別子の後に「: 型」を付けて適用する型を指定します。それぞれ";"で終わります。
宣言キーワードまたは実行文部に会うと本宣言は終了します。
【型付の定数宣言の例】
const
COUNT : ShortInt = 10; // 10
MAX : Integer = COUNT * 10; // 100
NUM : SmallInt = Succ(COUNT); // 11
MARKA : Char = 'A'; // 'A'(文字)
MARKB : String = 'B'; // 'B'(文字列)
VALUE : Single = 1 + 1.0; // 2.0
IMAX : Integer = MaxInt; // 2147483647 MaxInt は Systemで定義されている定数です。
TWOPI : Double = Pi * 2.0E3; // 6283.18530717958 Pi は Systemで用意されている関数です。
AT0 : Double = ArcTan(0); // 0.0 ArcTan は Syetemで用意されている関数です。
ASCII文字1文字の定数は、文字型か文字列型か明確にしたい場合は型付定数にします。
実数の定数では型付にするのが好ましいです。
例えば、以下の通常定数を宣言した場合、
@ VAL1 = 0.1;
A VAL2 = 0.5;
@はDoubeで、AはSingleになります。
@は二進数で表すと 0.0001100110011... のように循環小数となります。そのため多くの桁数を格納できる型として Double型になります。
Aは二進数で表すと 0.1 で有限小数となります。そのため、精度を落とさず格納できる最小の型として Single型となります。
定数も演算で使用されることを意識すると Double型で宣言するのが無難です。
@ VAL1: Double = 0.1;
A VAL2: Double = 0.5;
型を指定しない定数の演算では、以下のように期待する値とならないことがあるので注意が必要です。
【型を指定しない定数の計算1】
program prog1;
{$MODE OBJFPC}{$H+}{$J-} // 定数の変更禁止 {$J-} ディレクティブを追加設定。
{$CODEPAGE UTF8}
uses
SysUtils;
const
A = 10.0; // 型指定なし。Single型になる。
B = 3.0; // 型指定なし。Single型になる。
var
C : Double;
begin
C := A / B; // Sinle 型どうしの割り算。結果の制度は Single型。
WriteLn(C:20:14); // 出力: 3.33333325386047
end.
上記の例では、10.0 / 3.0 が 3.33333333333333 ではなく 3.33333325386047 となります。結果が Single 型の精度であることがわかります。
Single 型で計算した結果を Double 型の変数に代入しても、精度が上がるわけではありません。
この問題を回避するには「{$MINFPCONSTPREC 64}」ディレクティブを指定します。
このディレクティブを指定すると実数定数の最低の精度が 64bit(Double型)になります。
【型を指定しない定数の計算2】
program prog1;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64} // 実数の計算精度に関するディレクティブを追加設定。
{$CODEPAGE UTF8}
uses
SysUtils;
const
A = 10.0; // 型指定なし。ディレクティブにより Double型になる。
B = 3.0; // 型指定なし。ディレクティブにより Double型になる。
var
C : Double;
begin
C := A / B; // ディレクティブにより Double型どうしの割り算。結果の制度は Double型。
WriteLn(C:20:14); // 出力: 3.33333333333333
end.
特別な理由がなければ「{$MINFPCONSTPREC 64}」ディレクティブは設定することを推奨します。
【型を指定した定数の計算】
program prog1;
{$MODE OBJFPC}{$H+}{$J-}
{$CODEPAGE UTF8}
uses
SysUtils;
const
A : Double = 10.0; // Double型を指定。
B : Double = 3.0; // Double型を指定。
var
C : Double;
begin
C := A / B; // Double型どうしの割り算。結果の制度は Double型。
WriteLn(C:20:14); // 出力: 3.33333333333333
end.
上記の例では、「{$MINFPCONSTPREC 64}」を設定していませんが、10.0 / 3.0 が 3.33333333333333 となります。結果が Double型の精度であることがわかります。
実数の定数では型を指定するとにより、期待する制度で計算することができます。
型の宣言 |
|---|
型の宣言は、type キーワードの後に、識別子に "=" を使用してユーザ定義の型を宣言します。
ユーザ定義の型を使用できるとデータの扱い方法が豊富になり多様なアプリの開発ができます。
ここではユーザ定義型の例の一部を紹介します。
【型の宣言】
type
識別子1 = [type] 型1;
識別子2 = [type] 型2;
:
識別子n = [type] 型n;
識別子1〜識別子n がユーザ定義型です。型1〜型n はユーザ定義型に設定する型です。型の前の type はオプションです。それぞれ";"で終わります。
型は、単純型,構造型,ポインタ型,手続き型,文字列型,バリアント型,既存のユーザ定義型などから構成し、識別子がその型を表します。
宣言キーワードまたは実行文部に会うと本宣言は終了します。
既存の型の別名を宣言 |
|---|
【型に別名を付ける】
type
MyInt = Integer;
Money = Double;
型の前にオプションの type は設定しません。
MyInt型と Integer型は、型名が違うだけでまったく同じ型になります。MyInt型と Integer型を区別することはできません。
Money型と Double型の関係についても同様です。
この例ではあまり意味がないように思えますが、以下のような使い方をすると意味があります。
【別名の便利な例】
type
{$IFDEF CPU64}
MyInt = Int64; // MyInt型は64bit整数型になる。
{$ENDIF}
{$IFDEF CPU32}
MyInt = Int32; // MyInt型は32bit整数型になる。
{$ENDIF}
Money = Double;
この場合、MyInt型は、64bit環境では Int64型で、32bit環境では Int32型になります。MyInt型を使用したプログラムコード自体は64bit環境か32bit環境か意識しないですみます。プラットフォームによりデータのサイズが違う場合、このような宣言を利用します。
この例では、64bit環境でも32bit環境でもない場合、MyInt型は宣言されず存在しないことになります。
Moneyについては常に Double型として宣言されます。
既存の型から別の型を宣言 |
|---|
【既存の型から別の型を宣言する】
type
TMyInt = type Integer;
TMoney = type Double;
型の前にオプションの type を設定します。
単に別名を付けるのではなく、内容は同じだが違う型として宣言することになります。
TMyInt型は Integer型と同じ特性を持ちますが、型としては別の型という扱いになります。
通常の代入や計算などでは互換があり、TMyInt型と Integer型の変数で式を構成することができます。
TMoney型と Double型の関係についても同様です。
【別の型として宣言する効果の例】
program prog1;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
uses
SysUtils;
type
TMyInt = type Integer; // MyInt型は Integer型とは違う型の整数型となる。
var
A : Integer;
B : TMyInt;
procedure Func1(AVal: Integer); overload;
begin
WriteLn('Integer : ', AVal);
end;
procedure Func1(AVal: TMyInt); overload;
begin
WriteLn('TMyInt : ', AVal);
end;
begin
A := 10;
B := 20;
Func1(A); // 出力: Integer : 10
Func1(B); // 出力: TMyInt : 20
end.
TMyInt型と Integer型は同じ整数でも型としては区別されるため、Func1手続きをオーバーロードできるわけです。
ちなみに、Object Pascalでは他のユニット間でオーバーロードしない場合は「overload」キーワードを省略することができます。
type オプションを付けず「TMyInt = Integer」とした場合、コンパイル時にエラーとなります(Error: overloaded functions have the same parameter list)。
変数の宣言 |
|---|
変数の宣言は、var キーワードの後に、識別子に ":" を使用して変数に割り当て可能な型を宣言します。
通常の変数には、指定された型のデータが格納できる記憶域(メモリ)が割り当てられます。外部参照の場合は割り当てられません。
ここでは変数宣言の例の一部を紹介します。
【変数の宣言】
var
識別子1 : 型1 [ = 値1];
識別子2 : 型2 [ = 値2];
:
識別子n : 型n [ = 値n];
識別子1〜識別子n が変数です。型1〜型n は変数の型です。型の後の [ = 値n] はオプションで、変数の初期値を設定します。それぞれ";"で終わります。
型は、ユーザ定義型も含みます。初期値は、定数宣言の値と同様で、リテラルや定数などコンパイル時に評価可能である必要があります。
ユーザ定義型や初期値の定数は、変数宣言より前に宣言されている必要があります。
宣言キーワードまたは実行文部に会うと本宣言は終了します。
【変数宣言の例】
var
Counter : Integer; // 整数型の変数
XValue : Double; // 倍精度実数型の変数
I, J : Integer; // 同じ型の変数は","で並べてまとめて宣言することができる
UName : String; // 文字率型({$H+}が設定されている場合 String型は長い文字列の AnsiString型となる)
SName : String[30] // 短い(255バイト以内)文字列型
【初期値付き変数宣言の例】
var
Counter : Integer = 10;
XValue : Double = -3.15;
Pi2 : Double = 2.0 * Pi; // Piは Systemで定義されている円周率
MyName : String = '葛飾 北斎';
City : String[30] = 'Sapporo';
「I, J : Integer;」のように複数の変数を宣言する場合は初期値の設定はできません。
修飾子を指定した変数宣言 |
|---|
変数宣言では、さらに以下の修飾子を指定することができます。おもな目的は他言語インターフェースやDLLアクセスです。
cvar
Object Pascalの変数名(識別子)は大文字/小文字の区別がありませんが、cvar を付加すると記述した通りに大文字/小文字が区別される識別子になります。
C言語などのオブジェクトファイルとリンクする際に変数の修飾子 cvar を付加すると大文字/小文字を意識した参照ができるようになります。
external の指定がなければ初期値の設定は可能です。
external
自分では変数領域を確保せず、リンクするC言語などのオブジェクトファイルの変数に外部参照します。大文字/小文字の区別が必要な場合は cvar も指定します。
初期値の設定はできません。
external name '別名'
external に name で'別名'を指定すると、変数に別名を与えます。Object Pascal内では変数の識別子で参照しますが、リンクするC言語などのオブジェクトファイルの変数に外部参照する場合はこの'別名'を使用します。'別名'は大文字/小文字を区別するため、cvar の指定は不要です(指定不可です)。
初期値の設定はできません。
external 'DLL名'
自分では変数領域を確保せず、'DLL名'で示されるDLL内の変数に外部参照します。大文字/小文字の区別が必要な場合は cvar も指定します。
初期値の設定はできません。
external 'DLL名' name '別名'
external 'DLL名' に name で'別名'を指定すると、変数に別名を与えます。Object Pascal内では変数の識別子で参照しますが、DLLの変数に外部参照する場合はこの'別名'を使用します。'別名'は大文字/小文字を区別するため、cvar の指定は不要です(指定不可です)。
初期値の設定はできません。
export | public
export と public は同じです。宣言した変数が外部のC言語などのオブジェクトファイルから参照できるようにします。大文字/小文字の区別が必要な場合は cvar も指定します。
初期値の設定は可能です。
export name '別名' | public name '別名'
export や public に name で'別名'を指定すると、変数に別名を与えます。Object Pascal内では変数の識別子で参照しますが、リンクするC言語などのオブジェクトファイルからこの変数に参照する場合はこの'別名'を使用します。'別名'は大文字/小文字を区別するため、cvar の指定は不要です(指定不可です)。
初期値の設定はできません。
'別名' は Object Pascal上、単なる文字列のため Object Pascalの識別子として使用できない文字を含むことも可能です。
'別名' がある場合、外部オブジェクトやDLLの変数との結合は '別名' で行われます。
'別名' がない場合、外部オブジェクトやDLLの変数との結合は変数名で行われます。そのため宣言する変数名の大文字小文字は外部の変数に合わせます。
どの場合でも、Object Pascalプログラム内で変数を参照するする場合は、'別名' ではなく宣言した変数名を使用します。また、大文字小文字を無視した記述が可能です。
外部のオブジェクトファイルやDLLを使用する場合、名前の照合は行われますが、データの型やサイズについては照合されません。相互の型とサイズについては十分注意してください。不一致があると、無事にコンパイルと実行ができてもデータ領域を破壊する可能性があります。
【修飾子付き変数宣言の例】
var
Value0 : Integer; // ⓪ 通常の変数。修飾子なし。
Value1 : Integer; cvar; // @ 通常の変数。
Value2 : Integer; cvar; external; // A 外部オブジェクトの変数参照。
Value3 : Integer; external name 'nVal3'; // B 名前指定で外部オブジェクトの変数参照。
Value4 : Integer; cvar; external 'varslib.dll'; // C 外部DLLの変数参照。
Value5 : Integer; external 'varslib.dll' name 'nVal5'; // D 名前指定で外部DLLの変数参照。
Value6 : Integer; cvar; export; // E 変数を外部オブジェクトへ公開。
Value7 : Integer; cvar; public; // F Eと同じ。
Value8 : Integer; export name 'nVal8'; // G 名前指定で変数を外部オブジェクトへ公開。
Value9 : Integer; public name 'nVal9'; // H Gと同じ。
上記宣言の後、実行文部では Value0 〜 Value9 は、大文字小文字を無視した記述が可能です。
宣言部で外部との結合で使われる部分(変数名または'別名')だけが大文字小文字の区別が必要です。ACEFの変数名とBDGHの'別名'。
注意が必要な修飾子として absolute があります。
absolute
他の宣言済みの変数と同じ場所に重ねて変数を宣言します。型は同じである必要はありません。
宣言される変数に対しては記憶域は割り当てられません。単に同じ場所あり、宣言した型で参照できるというものです。
もし、宣言する型のサイズが参照する元の変数の型より大きい場合、はみ出た領域は確保されず未管理領域となります。元の変数と隣接する無関係の変数にかぶる可能性もあります。違う型での使用には細心の注意が必要です。
昔、DOS版の Turbo Pascalでは、物理メモリの絶対番地に変数を割り当てるために重宝しましたが、Windowsでは物理メモリの絶対番地への割り当てはできません。
absolute を使用する前にバリアントレコード(可変レコード)の使用を検討すべきと思います。
【absolute修飾子の例】
var
IValue : LongInt; // 32bit整数。
UValue : LongWord absolute IValue; // 32bit符号なし整数。
XValue : Int64 absolute IValue; // 64bit整数 - 不正な宣言。
IValueは普通に宣言された32bit整数です。
UValueは absoluteで IValueにかぶせた32bit符号なし整数です。同じサイズなので IValueと UValueは値を共有しつつ両方とも使用可能です。
XValueも absoluteで IValueにかぶせた64bit整数です。iValueに対して32bit多くアクセスしますがその分の領域は確保されません。XValueに値を代入するとはみ出た分のメモリに書き込まれてしまいます。たいていランタイムエラーにはならないので気が付かないことが多いと思います。要注意です。