Object Pascal 入門mcn-yokamoto/openwww/objpaspdf/manu5.pdf · 2004-05-20 ·...
Transcript of Object Pascal 入門mcn-yokamoto/openwww/objpaspdf/manu5.pdf · 2004-05-20 ·...
岡本安晴 2000.12.26;2004.4.20
5 - 1
Object Pascal 入門 その5
型
5‐1 単純型
5‐2 構造化型
5‐3 ポインタ型
5‐4 文字列型
5-5 手続き型
5-6 バリアント型
5-7 型の互換性・同一性
5-8 変数型キャスト
岡本安晴 2000.12.26;2004.4.20
5 - 2
5 型
データには整数や実数、文字列などいろいろな種類があり、それぞれコンピュータにおけ
る表され方が異なる。プログラムでは、このデータの種類は型として区別する。Object Pascalにおける型には、単純型、文字列型、構造化型、ポインタ型、手続き型、バリアント型などがある。まず、単純型から説明する。
5-1 単純型
単純型には、実数型と順序型がある。順序型には、整数型、文字型、論理型、列挙型、部
分範囲型があるが、順序型の場合はいずれの型であっても整数値との対応がある。 5-1.1 実数型
実数型では、数値は有効桁数を表す仮数と大きさを表す指数に分けて、
指数仮数 2×± の形によってコンピュータ内で表されている。実数型は、仮数と指数の表し方(ビット数な
ど)により以下の型が用意されている。
型 範囲 有効桁数 サイズ
(バイト数)
Real48 2.9×10-39 ~ 1.7×1038 11~12 6
Single 1.5×10-45 ~ 3.4×10^38 7~ 8 4
Double 5.0×10-324 ~ 1.7×10308 15~16 8
Extended 3.6×10-4951 ~ 1.1×104932 19~20 10
上の表にあげた他に、Real、Comp、Currencyがある。Real型は Delphi5では Double型と
して扱われる。Compと Currencyは実数型に分類されているが、Compは符号付 64ビットの整数型として表されており、順序型と動作が異なるため実数型に分類されているものであ
り、Currency も符号付 64 ビット整数型で表されているが、下位4桁が小数点以下を表すと解釈されるものであり、-922337203685477.5808 ~ 922337203685477.5807の範囲の数
を表す。
岡本安晴 2000.12.26;2004.4.20
5 - 3
5‐1.2 整数型
整数型は、整数値を表すもので、コンピュータでは2進数表記されている。1バイト(8ビ
ット)、2バイト(16ビット)、4バイト(32ビット)および8バイト(64ビット)
の大きさ(有効桁数)で表されるものがあり、最高位(左端)のビットが0であるか1で
あるかによって正負の整数値を表す符号付きのものと、0以上の整数のみを表す符号なし
のものがある。整数型演算の結果がその型の範囲を超えたときの現象については、付録5
「整数型の演算(オーバーフロー)」を参照せよ。 有効桁数と符号付き/符号なしの組み合わせによって、次の表に示す型が用意されてい
る。
型 範 囲 形 式
Shortint -128 ~ 127 符号付き 8 ビット
Smallint -32768 ~ 32767 符号付き 16 ビット
Longint -2147483648 ~ 2147483647 符号付き 32 ビット
Int64 -263 ~ 263-1 符号付き 64 ビット
Byte 0 ~ 255 符号なし 8 ビット
Word 0 ~ 65535 符号なし 16 ビット
Longword 0 ~ 4294967295 符号なし 32 ビット
なお、Delphi5では、型 Integerは Lonint、Cardinalは Longwordとみなされる。
整数値はプログラム中において10進数表記で書くことができるが、16進数で表すこ
ともできる。16進数表記は先頭に$を付けて
$ABCD12345
などと書く。 整数型に対しては、ビットごとに演算を行うビット演算子が次のように用意されている。
岡本安晴 2000.12.26;2004.4.20
5 - 4
Not X
X の値をビット単位で反転するビットごとの否定
X and Y
X と Y のビットごとの論理積
X or Y
X と Y の ビットごとの論理和
X xor Y
X と Y の ビットごとの排他的論理和
X shl i
X のビットごとの左シフトを i回行う
Y shr i
Y のビットごとの右シフトを i回行う
ビット演算とは、例えばnビットで表される整数値 Xを
岡本安晴 2000.12.26;2004.4.20
5 - 5
・ ・ ・ ・ ・ ・
と表し、Xにビット演算 notを行った結果を
と表したとき、ビット単位で
ii rbnot = という演算を行うものである。 整数 Xと整数 Yに対するビット演算opの結果を
YopXZ =
とおき、Yと Zを
および
と表したとき、ビット単位で
iii yopxz = が成り立つ。 5-1.3 文字型
岡本安晴 2000.12.26;2004.4.20
5 - 6
文字は、コンピュータ内では整数値と対応付けて表す。この対応付け方の違いにより、
Delphi では、1文字を1バイトの整数値に対応けて表す AnsiChar 型と、2バイトの整数
値に対応けて表す WideChar型が用意されている。AnsiChar型では ANSI 規格に従って文字
が表されている(整数値と対応付けられている)。例えば、文字 Aは整数値65によって表
される。WideChar型では Unicode 規格に従って文字が表され、この型の文字はワイド文字と
もいう。また、Delphi5においては、Char型は AnsiChar型と同じである。 1バイトの整数値に対応する Char型、あるいは AnsiChar型の文字は、関数 Chrによっ
て求めることができる。 Chr(65)
は、文字 ‘A’
を表す。整数値に対応する文字は整数値の前に記号#を付けて表すこともできる。Chr(65)は #65
と同じ文字 ’A’を表す。 逆に、文字に対応する整数値は、関数 Ordで求めることができる。
Ord(‘A’)
の値は、整数値 65
である。 5-1.4 列挙型
列挙型は、識別子(名前)を値とするものである。列挙型の値として使用する識別子を
宣言するときは、値として用いる識別子をコンマ,で区切って並べ括弧( )で囲む。次の
列挙型
(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
は、Sunday、・・・、Saturdayの各識別子を値とする型を表す。 したがって、
var w : (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);
と宣言された変数 wには、
岡本安晴 2000.12.26;2004.4.20
5 - 7
w:=Sunday;
などの代入が行える。上の代入によって、変数 wの値は Sundayになる。 上の曜日名を値とする型を2箇所以上の変数宣言において用いるときは、その型に名前
(型名)を与えてその名前によって宣言を行う。名前によらず、識別子の並びを( )で
囲んだ形の列挙型を繰り返して用いた場合、値として用いる同じ識別子が複数の列挙型の
宣言に現れることになるのでエラーとなる。すなわち、
var a : (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);
var b : (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);
と宣言することはできない。上の宣言の場合、変数 aの宣言における
(Sunday,・・・)
と、変数 bの宣言における
(Sunday,・・・)
は異なる列挙型であるとみなされ、これらの列挙型に同じ識別子 Sunday などが使われているのでエラー(識別子の多重定義エラー)となる。異なる列挙型同士の間で同じ識別子
を共通の値とすることはできない。
var a, b : (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday); と宣言するか、あるいは、型に type TWeek = (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);
と名前を与えて、
var a : TWeek;
var b : TWeek; とする。 typeで始まるものは型宣言部といい、型に名前(識別子)を与えるのに用いる。型宣言
部は、次の形である。
岡本安晴 2000.12.26;2004.4.20
5 - 8
型宣言部
Type 識別子 = 型; % ・・・ 識別子 = 型 ; %
列挙型は次の形
列挙型
( 識別子リスト )
であるが、識別子リストに並べられた識別子には左から順番に0からの整数値が対応付け
られている。この列挙型の値である識別子に対応付けられている整数値(順序値という)
は、関数 Ordによって知ることができる。上の TWeek型の場合、
Ord(Sunday)
の値は
0 であり、
Ord(Saturday)
の値は 6
である。 列挙型を含めた順序型に対して、関数 Ord などの他に以下の関数が用意されている。こ
こで、順序型の値の前の値とは順序値が1小さい値のことであり、後の値とは順序値が1
大きい値のことである。
岡本安晴 2000.12.26;2004.4.20
5 - 9
Ord(順序型の式)
式の値の順序値を返す
Pred(順序型の式)
式の値の前の値を返す
Succ(順序型の式)
式の値の後の値を返す
High(順序型の型識別子または順序型の変数)
型のもっとも大きい値を返す
Low(順序型の型識別子または順序型の変数)
型のもっとも小さい値を返す
順序型の値は、同じ順序値(順序型の値に対応付けられている整数値、整数型の場合はその整数値)をもつ他の順序型の値に変換できる。これを値型キャストという。値型キャ
ストによって他の順序型の値に変換するときは、変換したい値を( )で囲み、その前に
変換後の型名を付ける。上の列挙型 TWeekの場合、 TWeek(1)
の値は Monday
である。 逆に、
Longint(Tuesday)
の値は 2
である。
岡本安晴 2000.12.26;2004.4.20
5 - 10
値型キャストは、型キャストと呼ばれるものの 1 つである。型キャストは、次の形のものである。
型キャスト
型識別子(式)
値型キャストでは、型識別子として順序型に対するもの、( )内の式も順序型の値をとる
ものを設定する。 型キャストには、値型キャストの他に、変数型キャストがある。変数型キャストは、5.8
節「変数型キャスト」において説明する。 5-1.5 論理型
論理型は、2つの値、True および False、をとる型である。論理型は、通常 Boolean 型が
用いられるが、Windows 環境や他の言語との互換性をとるために ByteBool、WordBool、
LongBoolの各型が用意されている。Booleanと ByteBoolは1バイト、WordBoolは2バイト、
LongBoolは4バイトを占める型である。Boolean型においては、Falseの順序値は0、True
の順序値は1である。ByteBool、WordBoolおよび LongBoolの各型においては、Falseの順
序値は0、Trueの順序値は0以外である。 論理型の値に対する演算子として、次の表のものがある。
A B not A A and B A or B A xor B
True True False True True False
True False False False True True
False True True False True True
False False True False False False
上の表は、Aと Bの各値の組み合わせ(not A のときは Aの値)に対する演算の値が、それ
ぞれの演算の列に示されている。例えば、Aの値が True、Bの値が Falseのときの「A xor B」
の値は、Aの列における値が True、Bの列における値が Falseである行と、「A xor B」の列
岡本安晴 2000.12.26;2004.4.20
5 - 11
との交わりの位置にある値として Trueであることが示されている。 5-1.6 部分範囲型
既に定義されている順序型のとる値の範囲の一部分を値とする型を与えることができる。
これを部分範囲型という。部分範囲型は、そのとりうる値の最小値と最大値を..でつない
で表す。
Type TSub = 1..9;
と宣言すると、型 TSubは1から9までの範囲の整数値をとる型を表す。
Var x : 1..5;
と宣言された変数xは1から5までの値をとるものである。 部分範囲型は、次のように与えられる。
部分範囲型
定数式 1 ..定数式 2
上の部分範囲型は、定数式1で与えられる値から定数式2で与えられる値までの範囲の値
をとるものとして定義される。
岡本安晴 2000.12.26;2004.4.20
5 - 12
5-2 構造化型
構造化型には、集合型,配列型,レコード型,ファイル型,クラス型,クラス参照型,お
よびインターフェース型がある。本節では、集合型、配列型、レコード型およびファイル
型について説明する。クラス型については、第7章「オブジェクトとクラス型」において
説明する。 5-2.1 集合型
順序型の値を要素とする集合を値とする型を集合型として宣言することができる。例えば、
1から9までの整数値を要素とする集合を値とする型は、
set of 1..9
で与えられる。したがって、
var x : set of 1..9;
と宣言された変数 x は、1から9までの整数値を要素とする集合を値とする。集合は、そ
の要素をコンマ,で区切って並べたものを[ ]で囲んで表す。したがって、次の代入文
x:=[1,3];
により、xは1と3を要素とする集合をその値とする。 集合は最大256個の要素をもち、要素の順序値は0以上255以下でなければならな
い。集合型は、一般に次の形式で与えられる。
集合型
set of 順序型
ただし、上の順序型の値の順序値は0以上255以下であるものとする。したがって、整
数値を要素とする集合は0以上255以下の整数値を要素とするものでなければならない。 集合は要素をコンマ,で区切って並べ[ ]で囲むことにより表すが、要素の順序値が連続
しているときは、連続している値の最小値と最大値を..でつないで表すことができる。例
岡本安晴 2000.12.26;2004.4.20
5 - 13
えば,
[1,3,4,5,6,7,9]
は、 [1,3..7,9]
と同じ集合を表す。 集合の要素は、式で表すことができる。したがって、
[1 .. 5]
は [1 .. 10 div 2]
と同じである。 空集合は、何も要素を書かない
[ ]
で表す。 集合型に対する演算として、以下のものがある。
岡本安晴 2000.12.26;2004.4.20
5 - 14
演 算 結 果
集合 1 + 集合 2 集合 1 と集合 2 の和集合
集合 1 - 集合 2 集合 1 と集合 2 の差集合
集合 1 * 集合 2 集合 1 と集合 2 の積集合
集合 1 <= 集合 2 集合 1 が集合 2 に含まれるとき論理型の値 True
それ以外のとき False をとる
集合 1 >= 集合 2 集合 1 が集合 2 を含むとき論理型の値 True
それ以外のとき False をとる
集合 1 = 集合 2 集合 1 が集合 2 に等しいとき論理型の値 True
それ以外のとき False をとる
集合 1 <> 集合 2 集合 1 が集合 2 に等しくないとき論理型の値 True
それ以外のとき False をとる
要素 in 集合 要素が集合に属するとき論理型の値 True
それ以外のとき False をとる
5-2.2 配列型
配列型(配列を表す型)は、同じ型の変数を通し番号をつけてまとめたものである。配列
には、静的配列と動的配列がある。まず、静的配列について説明する。
静的配列
下図は、実数型の変数を3つ並べたものをまとめた配列を表している。
岡本安晴 2000.12.26;2004.4.20
5 - 15
配列全体を識別子 xで表し,1つ 1つの変数は x[1],x[2],x[2]と識別子の右側に番号を[ ]
で囲んで付けて表している。上の配列 xは、次のように宣言する。
Var x : array[1..3] of extended;
x[1]などの配列の中の一つ一つの変数は、配列の要素という。一番目と2番目の要素に
10.0と 20.0を代入して、その和を3番目の要素に代入する操作は、次のように書ける。
x[1]:=10.0;
x[2]:=20.0;
x[3]=x[1]+x[2];
配列型は、一般には次のように宣言する。
配列型
array[添字型 %, ..., 添字型%] of 型
各添字型は、値の範囲が2GB(231バイト)を超えない順序型である。ただし、これは
文法上の制約であって,実際にプログラミングを行うときは使用しているシステムのメモ
リの大きさなどによる制限を受ける。ofの後の型は、要素の型を示す。この型は、配列に
おける基本型という。基本型として配列型をとる
array[1..2] of array[1..3] of Longint;
岡本安晴 2000.12.26;2004.4.20
5 - 16
は、
array[1..2,1..3] of Longint;
と同じものである。
また、
var z : array[1..2] of array[1..3] of Longint;
として宣言された配列 zの要素
z[1][2]
は、
z[1,2]
と表すこともできる。
z[1]
は
array[1..3] of Longint
型の配列を表す。これらのことを図示すると、次のようになる。
次の列挙型 TWeek
Type TWeek = (sun,mon,tue,wed,thu,fri,sat);
に対して
var w, w1 : array[TWeek] of string;
岡本安晴 2000.12.26;2004.4.20
5 - 17
と配列 wを宣言すると、
w[sun]:=’日曜日’;
w[mon]:=’月曜日’;
・
・
・
w[sat]:=’土曜日’;
などと wの各要素に対して代入ができる。
w1:=w;
と配列変数の名前で代入を行うと、wの各要素の値が対応する w1の各要素に代入される。
すなわち、
w1[sun]
の値は
‘日曜日’
に設定され,
w1[sat]
の値は
‘土曜日’
に設定される。
配列型の変数の代入は、同じ型同士でなければならない。要素の型と数が同じであって
も
var x : array[1..3] of extended;
y : array[1..3] of extended;
と別々に宣言された場合は、xと yは異なる型であるとみなされる。配列型に名前を与え、
変数宣言における型をその名前で指定すると同じ型として扱われる。例えば、次のように
する。
Type tarray = array[1..3] of extended;
var x : tarray;
y : tarray;
xと yが同じ型であるようにするだけであれば、
岡本安晴 2000.12.26;2004.4.20
5 - 18
var x, y : array[1..3] of extended;
と宣言してもよい。
xと yが同じ型であれば
x:=y;
という代入が可能である。
練習問題 5-2.1
n個のデータの平均値と標準偏差を求めるプログラムを作成せよ。例えば,まず最初に提
示するインプットダイアログボックスでnの値を入力し,続いてn回インプットダイアロ
グボックスを提示してn個の値を入力するものとせよ。n個の値の入力後、メッセージボ
ックスにより、求めた平均値と標準偏差を表示せよ。入力データの値は、十分に大きいサ
イズの配列dataを宣言しておいて、その1番目からn番目までの各要素に順番に格納せよ。
ただし,
∑=
=n
i
idata1
][µ平均値 ∑=
−=n
iidata
nSD
1
2)][(1 µ標準偏差
である。
動的配列
静的配列では、添字型のとる値の範囲は配列型の宣言時に決まる。動的配列では、添字型
のとる値の範囲はプログラムの実行時に設定される。ただし、添字型は整数型で、値は0
から始まるものに限られる。
動的配列型は、次のように宣言される。
動的配列型
array of % ...array of % 型
動的配列型として宣言された変数は、ポインタ(ポインタについては5-3節「ポインタ型」
において説明する)のように働く。動的配列としての配列は、プログラムの実行時に手続
き SetLengthによって設定される。例えば,動的配列型の変数 xが
岡本安晴 2000.12.26;2004.4.20
5 - 19
var x : array of extended;
と宣言されているとき、プログラムの実行時に
SetLength(x,10);
が実行されることにより、基本型(動的配列の要素の型)が extendedである長さ(配列の
要素の数)が10の動的配列がメモリー上に確保されて、xにより指し示される。確保され
た配列の各要素は、
x[0],x[1],・・・,x[9]
という形で表される。添字が0から始まっていることに注意。
確保された配列をメモリーから開放する(廃棄する)ときは、nilを代入するか、手続き
Finalizeを実行する。すなわち、上の動的配列型の変数 xの場合、
x:=nil;
または
Finalize(x);
を実行する。
動的配列型の変数はポインタのように働く。したがって、動的配列型の変数の代入は、
静的配列の場合と異なり、配列の要素全体の代入ではなく、アドレスの代入、すなわち動的
配列型の変数の指し示すところが同じになる。例えば,
var a, b : array of Longint;
と宣言されているとき
SetLength(a,5);
a[0]:=1;
b:=a;
とすると、bは aと同じ配列を指し示すことになる。したがって、b[0]の値は1であるが、
この後
b[0]:=2;
岡本安晴 2000.12.26;2004.4.20
5 - 20
を実行すると、a[0]の値も2になる。これは b:=aの代入によって、bは aと同じ動的配列
を指し示しているからである。このため、b[0]への代入は、a[0]への代入となっている。
上の例は1次元の動的配列であるが、多次元動的配列の場合も同様である。この場合も、
動的配列の確保は手続き SetLengthで行う。例えば、
var a : array of array of extended;
と宣言されているとき、行数と列数を指定して
SetLength(a, 5, 7);
と動的配列を確保することができる。
あるいは、行数だけ先に
SetLength(a,5);
と設定することもできる。この場合は、行ごとに列数を変えることができる。例えば、
for i:=1 to 5 do SetLength(a[i-1], 2*i);
とすると、第 i-1 行の列数は 2*i 列になる。
多次元動的配列の要素は、静的配列の場合と同様に、多次元動的配列型の変数の右側に
行位置と列位置を示す値をコンマ,で区切って並べ[ ]で囲んだものを付けることによっ
て表す。例えば、上の例の場合、
a[1,2]
は1行2列目の要素を表す。これらの位置は、0から数えることに注意。
確保した多次元動的配列の開放(廃棄)は、1次元動的配列と同じく nil を代入するか
手続き Finalizeを用いる。
練習問題 5-2.2
練習問題 5-2.1 で作成したプログラムを動的配列を用いたものに改めよ。
オープン配列パラメータ
岡本安晴 2000.12.26;2004.4.20
5 - 21
動的配列に似たものとしてオープン配列パラメータと呼ばれているものがある。次のよ
うに、手続き・関数ヘッダーのパラメータリストにおいて用いられる。
function sum( a : array of extended ) : extended;
オープン配列パラメータは、動的配列に似ている(形式が同じ)が異なるものとして区別
されるものである。オープン配列パラメータの実パラメータとしては、任意サイズの配列、
オープン配列パラメータにおける基本型(要素の型)の変数、オープン配列コンストラク
タを渡すことができる。オープン配列コンストラクタとは、値(式)をコンマ,で区切って
並べたものを[ ]で囲んだものである。
オープン配列コンストラクタ
[ ? 式 % , ..., 式 % ? ]
オープン配列コンストラクタは、オープン配列パラメータの実パラメータとして用いられ
たとき、[ ]内の値を要素とする配列のように機能する。オープン配列パラメータに対し
て要素(パラメータ)ごとに異なった型の値を渡すときは、型可変オープン配列パラメー
タを用いる。型可変オープン配列パラメータは、
procedure test( a : array of const );
のように宣言する。
次の例は、オープン配列パラメータをもつ手続き sumの例である。
var r : array[1..10] of extended
= (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
c : extended = 9;
procedure TForm1.Button1Click(Sender: TObject);
var s : extended;
岡本安晴 2000.12.26;2004.4.20
5 - 22
function sum( a : array of extended ) : extended;
var i : Longint;
v : extended;
begin
v:=0;
for i:=0 to High(a) do v:=v+a[i];
sum:=v;
end;
begin
// 配列を実パラメータとする
s:=sum(r);
ShowMessage('Sum(r) = '+FloatToStrF(s,ffGeneral,9,1));
// 値を実パラメータとする
s:=sum(c);
ShowMessage('Sum(c) = '+FloatToStrF(s,ffGeneral,9,1));
// オープン配列コンストラクタを実パラメータとする
s:=sum([100.0, 20.0, 3.0]);
ShowMessage('Sum([100+20+3]) = '+FloatToStrF(s,ffGeneral,9,1));
Close;
end;
サンプルプログラム PSample5a1.dpr は、上の例を実行するものである。
5‐2.3 レコード型
配列型は同じ型のものを通し番号を付けてまとめたものであるが、レコード型では異なっ
た型のデータをまとめることができる。例えば、学生番号を表す整数型の変数 IDと名前を
表す文字列型の変数 Nameをまとめたものは、
record
ID : Longint;
Name : string;
岡本安晴 2000.12.26;2004.4.20
5 - 23
end;
というレコード型として宣言することができる。
上のレコード型の変数 rと sを
var r, s : record
ID : Longint;
Name : string;
end;
と宣言すると、変数 rおよび s内の変数 IDと Name(レコードのフィールドという)に値を
代入することは、
r.ID:=1; r.Name:=’名前1’;
s.ID:=2; s.Name:=’名前2’;
というように書く。レコード型の変数にピリオド.を付けてフィールドの前に置いて、どの
レコード型の変数のフィールドであるのかを示す。 同じ変数内のフィールドを何回も指し示すときは、with文を用いて、例えば
with r do
begin
ID:=1; Name:=’名前1’;
end;
と書くことができる。 with文を次の形
with レコード型変数 do 文;
岡本安晴 2000.12.26;2004.4.20
5 - 24
で用いると、do の右に置かれた文においては、with と do の間にあるレコード型変数を省
略してそのフィールドを指し示すことができる。 withと doの間に複数のレコード型変数名がコンマ,で区切って並べられたものは、with
が階層的に用いられたものと同じである。例えば、
with r1, r2, r3 do 文;
は、 with r1 do with r2 do with r3 do 文;
と同じである。 レコード型は、一般的には次のような形式である。
レコード型
case ? フィールドリスト ? end
ここで、フィールドリストは次のように与えられる。
フィールドリスト
? フィールド宣言 % ;... ;フィールド宣言?;? % ? ? レコード可変部 ?;? ?
すなわち、フィールド宣言をセミコロン;で区切って並べた後にレコード可変部を付けた
形である。フィールド宣言あるいはレコード可変部は省略することができる。
フィールド宣言は、次の形のものである。
フィールド宣言
岡本安晴 2000.12.26;2004.4.20
5 - 25
識別子リスト : 型
識別子リストは、識別子をコンマ,で区切って並べたものである。
レコード可変部は、次の形のものである。
レコード可変部
case ? 識別子:? 型 of 可変部 % ;… 可変部 % ?;?
ofの前の型は、Longintなどの順序型を表す識別子(型名)で与える。型の左の識別子は、
変数を表わすがタグという。可変部は、次の形で与える。
可変部
定数式 % , … , 定数式 % : ( ? フィールドリスト ? )
次のレコード型は、レコード可変部のみをもつものである。
record
case b : byte of
0 : (i : Longint);
1,9 : (a : extended)
end;
これは、タグ bを省略して、
record
case byte of
0 : (i : Longint);
1,9 : (a : extended)
岡本安晴 2000.12.26;2004.4.20
5 - 26
end;
としても同じである。
レコード可変部に記述されている各可変部は、メモリー上の同じ位置に置かれる。可変
部のサイズが異なるときは、それぞれのメモリー上の先頭の位置がそろえられる。例えば、
次のレコード型の変数 rの宣言
var r : record
case byte of
0 : ( i : Longint );
1, 9 : ( a : extended );
10 : ( b : array[0..9] of byte );
end;
の場合、各可変部のフィールド、i、a、b、は、メモリー上の同じ場所に、先頭の位置をそ
ろえて割り付けられる。
したがって、
r.i:=1;
を実行すると、r.b[0]は1、r.b[3]は0になるが、
r.i:=-1;
岡本安晴 2000.12.26;2004.4.20
5 - 27
を実行すると、r.b[0]、r.b[3]ともに 255 になる。このことは、例えば、次のプログラム
を実行すると確認できる。
with r do
begin
i:=1;
ShowMessage('r.i = 1 --> b[0] = '+IntToStr(b[0])+
' b[3] = '+IntToStr(b[3]));
i:=-1;
ShowMessage('r.i = -1 --> b[0] = '+IntToStr(b[0])+
' b[3] = '+IntToStr(b[3]));
end;
サンプルプログラム PSample5b1.dpr は、上のことを確認するものである。
5‐2.4 ファイル型
ファイル型変数によってハードディスクなどのファイルへの入出力を行うことができる。
ファイルの操作は、Windowsのファイルハンドルを用いる方法,VCLのファイルストリームを用いる方法、型なしファイルを用いる方法もあるが、ここではファイル型変数を用い
る方法について説明する。
岡本安晴 2000.12.26;2004.4.20
5 - 28
ファイル型の変数(ファイル変数)は、次のように宣言する。
var f : file of extended;
上のファイル型の変数 fは、extended型のデータの入出力に用いられる。extended型の
変数 vの値を書き出すときは write(f,v);
変数 vに読む込むときは read(f,v);
とする。 ファイル型は、一般に次の形で与える。
ファイル型
file of 型
file ofに続く型は、Longintのように型を表す識別子をおく。この型は、メモリー上での
大きさが固定されているものでなければならない。また、ポインタ型も許されない。 ファイル変数 f は、ファイルへの入出力の前に、具体的なファイルに結び付けておく必
要がある。このファイル変数とファイルとの関連付けは、手続きAssignFileによって行う。
上のファイル変数 fをファイル「c:\MyData.dat」に結びつけるときは
AssignFile( f, ‘c:\MyData.dat’ );
とする。 ファイルの関連付けの後,出力のときは手続き Rewrite、入力のときは手続き Resetを実
行しておく。すなわち、 Rewrite(f);
あるいは Reset(f);
などとする。 Rewriteの実行により、ファイル変数 fと関連付けられているファイル名のファイルが作成され,書き出し位置がファイルの先頭に置かれる。同じ名前のファイルが既に存在して
岡本安晴 2000.12.26;2004.4.20
5 - 29
いるときは、そのファイルは削除されてファイル変数と関連付けられた名前のファイルが
新しく作成される。 Resetを実行すると、ファイル変数と関連付けられているファイル名のファイルが開かれ、
読み込み位置がファイルの先頭に置かれる。ファイルからの読み込みが進んで、読み込み
位置がファイルの最後に達すると、関数 EOFは Trueを返す。上の場合,
EOF(f)
の値が Trueになる。読み込み位置がファイルの最後にないときは、EOFは Falseを返す。 ファイルへの入出力が終わったときは、手続き CloseFile を実行してファイル変数とフ
ァイル名との関連付けを終了しておく。ファイルへの書き出しを行っているときは、
CloseFileの実行により,ファイルへの出力が完了されて関連付けが終了する。上のファイ
ル変数 fの例の場合は、
CloseFile(f);
というように CloseFileを実行する。 ここで説明したファイル入出力関係の手続き・関数を一般的にまとめると、次のように
なる。
procedure AssignFile(ファイル変数, ファイル名); ファイル変数をファイル名に結び付ける。
procedure Rewrite(ファイル変数 ); ファイル変数と結び付けられているファイル名のファイルを作成して、書き込み位置
をそのファイルの先頭に置く。 procedure Reset(ファイル変数 );
ファイル変数と結び付けられているファイル名のファイルを開いて読み込み位置をフ
ァイルの先頭に置く。 procedure Write(ファイル変数, 変数 %, … , 変数% );
ファイル変数と結び付けられているファイルの現在の書き込み位置から順番に変数の
値を書き出す。 procedure Read(ファイル変数, 変数 %, … , 変数% );
ファイル変数と結び付けられているファイルの現在の読み込み位置から順番に変数に
値を読み込む。 procedure CloseFile(ファイル変数);
岡本安晴 2000.12.26;2004.4.20
5 - 30
ファイル変数とファイル名の結び付きを終了する。ファイルへの書き出しのときはフ
ァイルへの出力を完了してから結び付けを終了する。
function Eof(ファイル変数) : Boolean; ファイル変数と結び付けられているファイルの読み込み位置がファイルの最後にある
と True、それ以外のときは Falseを返す。 read および write 手続きのパラメータにおけるファイル変数の基になる型(file of に
続く型)と入出力用変数の型は同じでなければならない。このことから file of に続く型
と入出力用変数の型は、同じ識別子(型名)で与えなければならないことがわかる。 extended型のデータが格納されているファイル c:\sample.datからデータを 1つ読み込み、それをファイル c:\copy.datに書き出すプログラムは、次のようになる。 ファイル型変数 fと extended型変数 vを次
var f : file of extended;
v : extended; のように宣言しておいて
AssignFile(f,’c:\sample.dat’);
Reset(f);
Read(f,v);
CloseFile(f);
AssignFile(f,’c:\copy.dat’);
Rewrite(f);
Write(f,v);
CloseFile(f); とする。 テキストファイル テキストファイルの入出力(文字列データによる入出力)用ファイル型として TextFile
型が用意されている。TextFile型については、付録4「テキスト(文字列)の簡単な入出
力」において説明する。 練習問題 5‐2.4
岡本安晴 2000.12.26;2004.4.20
5 - 31
次のデータファイル
-1 1 10 2 20 3 30 4 40 5 50 6 60 7 80 8 80 9 90 10 100 -10
を読み込み、平均値、標準偏差を求めよ。求めた値を、読み込んだデータとともにテキス
トファイルに書き出せ。ただし、1 行目の-1 と最終行の-10 はデータの始まりと終わりを示す値である。すなわち、1行目で読み込まれた値より小さい値が読み込まれると、デ
ータが全て読み込まれたと判断される。 解答例 PEx5241.dpr は、ファイル名をインプットダイアログボックスによって入力する
ものである。ファイル名の設定は、Delphiのコンポーネントパレットの Dialogs ページに
ある OpenDialog コンポーネントあるいは SaveDialog コンポーネントを用いると簡単にで
きる。これらのコンポーネントを使用した例が PEx5242.dpr である。
岡本安晴 2000.12.26;2004.4.20
5 - 32
5‐3 ポインタ型
ポインタ型とは、データ(変数)のメモリ上の位置(アドレスという)を指し示すもので
ある。データの型に対応したポインタ型が用いられる。データの型に対応するポインタ型
は、そのデータの型の前に^を付けて表される。従って、ポインタ型は次のように宣言さ
れる。
Type ポインタ型 = ^データの型;
Longint型のデータを指し示すポインタ型は、例えば次のように宣言される。
Type PLongint = ^Longint;
このとき、TPLongint型のポインタ型変数は Longint型のデータ(変数)の場所を指し示す。
Var x : Longint;
P : PLongint;
と宣言されているとき、
P:=@x;
により、Pは変数 xの場所を指し示す。@を変数の前に付けると、その変数のアドレスが得
られる。ポインタ型の変数の指し示している場所は、ポインタ型の変数の後ろに^をつける
ことによって表される。したがって、上の例の場合、
P^
は
x
を表すことになるので、
P^:=1;
によって、xの値は 1になる。
上図は、P、P^、x の関係を示すものである。上のことを示すサンプルプログラムを
岡本安晴 2000.12.26;2004.4.20
5 - 33
PSample51.dprとして用意した。
手続き New を用いると、ポインタ型変数に対応する型のデータの場所を確保することが
できる。
New(ポインタ型変数);
を実行すると、ポインタ型変数に対応する型のデータの場所が確保され、ポインタ型変数
はその確保された場所(動的変数という)を指し示す。したがって、上のポインタ型変数 P
に対して、
New(P);
を実行すると、
P^
は手続き Newによって確保された場所(動的変数)を表す。
確保した場所は、手続き Disposeによって開放することができる。Pの指し示している場
所は、
Dispose(P);
の実行によって開放される。プログラムの実行において確保された動的変数は、プログラ
ムの実行終了の前に開放しておく。
ポインタ型の変数が何も指し示していないときの値は、
nil
である。
手続き Newと Disposeの使用例を、サンプルプログラム PSample52.dprに示した。
レコード型のフィールドにポインタ型を含ませると、レコード型の動的変数をつなぐこ
とができる(リスト構造)。
サンプルプログラム PSample53.dprでは、レコード型 TRecが次のように宣言されている。
type PRec = ^TRec;
TRec = record
Name : string;
Ptr : PRec;
end;
フィールド Nameに文字列を格納し、ポインタ型 PRecのフィールド Ptrによってレコード
型 TRecのデータ(動的変数)をつなぐ。ポインタ型 PRecの宣言において、レコード型 TRec
岡本安晴 2000.12.26;2004.4.20
5 - 34
がその TRec型の宣言に先立って用いられている。Object Pascal では、既に宣言されたも
のを用いるという原則があるが、上の場合のようなレコード型とポインタ型の関係の場合
は、後で宣言されているレコード型名をポインタ型の宣言において用いることができる。
レコード型 TRecは、図5.1のイメージ(線形リストという)で用いられている。
図5.1 線形リスト
サンプルプログラム PSample53.dprでは、まず、フィールド Nameに格納する名前(文字
列)の入力が求められる。1つの名前の入力に対して1つのTRec型の動的変数が対応する。
空の文字列(0個の文字)が入力されると、名前の入力終了とみなされる。1つ1つの動
的配列は、生成された順番にポインタ型フィールド Ptrによって図5.1のように連結され
ている。このことは次のようにして行われている。
New(P);
P^.Ptr:=nil;
ck:=false;
repeat
with P^ do
begin
Name:=InputBox('','名前','');
if Length(Name) < 1
then
ck:=true
else
begin
New(Q);
Q^.Ptr:=P;
P:=Q;
end;
end;
until ck;
岡本安晴 2000.12.26;2004.4.20
5 - 35
上の処理の内容を図示すると図5.2のようになる。
図5.2 a New(Q)の実行で動的変数を生成
図5.2 b Q^を P^に連結
図5.2 c P が Q^を指し示すようにする
図5.2 動的変数の線形リストへの追加
まず、動的変数を New(P)の実行によって生成する。この最初の動的変数は、線形リストの
端点として用いる。端点であることを示すために、ポインタ型フィールド Ptr に nil を代
入しておく。その後、InputBoxによって入力した文字列を Nameに設定し、Nameに設定され
た文字列の長さが1以上であればNew(Q)を実行して新しく動的変数を生成する(図5.2a)。
生成した動的変数 Q^を P^に連結するために、Q^のポインタ型フィールド Ptrに P^のアドレ
岡本安晴 2000.12.26;2004.4.20
5 - 36
スを設定する(図5.2 b)。次に、新しく生成した動的変数を Pが指し示すようにしてから
(P:=Q;図5.2 c)、この新しい Pの値に対して、InputBoxによる文字列の入力に戻る。
図5.2の線形リストのデータは、次の手順で後ろのものから表示・削除される。
while P^.Ptr <> nil do
begin
Q:=P;
P:=Q^.Ptr;
Dispose(Q);
ShowMessage('名前 = '+P^.Name);
end;
Dispose(P);
上の処理を図示すると図5.3のようになる。
図5.3 a P^が次の動的変数に連結されている。
図5.3 b Q が P^を指し示すようにする。
岡本安晴 2000.12.26;2004.4.20
5 - 37
図5.3 c P が P^(Q^)の次の動的変数を指し示すようにする。
図5.3 d Q^の廃棄。
図5.3 線形リストの廃棄。
P^が線形リストの最初の端点でない(P^.Ptr <> nil;図5.3 a)ならば、Qが P^を指し示
す用に設定した後(図5.3 b)、Pは次の動的変数を指し示すようにする(図5.3 c)。そ
の後、Q^を廃棄し(図5.3 d)、P^が端点であるかどうかのチェックに戻る。P^が端点であ
れば(P^.Ptr = nil)、while 文を終了して、端点 P^を廃棄する。この端点の廃棄により、
図5.2における線形リスト上の動的変数の廃棄はすべて完了する。
岡本安晴 2000.12.26;2004.4.20
5 - 38
5‐4 文字列型
文字列を扱う型は、文字列型として、ShortString、AnsiString、WideStringが用意されて
いる。
ShortString型は、文字列の長さが255文字までの文字列を扱う。
AnsiString型は、文字列の長さが231文字までの文字列を扱う。string型はデフォルト
で AnsiString型とみなされる。
WideString型は文字列の長さが230文字までの文字列を扱う。
ShortString型と AnsiString型においては、文字列における各文字は AnsiChar型である
が、WideString型における各文字は WideChar型である。
変数sが文字列型であるとき、sに格納されている文字列の第i番目の文字は
s[i]
によって表される。すなわち、文字列型の変数における第i番目の文字は、配列型の変数
の第i番目の要素と同じように扱われる。sが ShortString型あるいは AnsiString型であ
れば s[i]は AnsiChar型であり、sが WideString型であれば s[i]は WideChar型の文字で
ある。
文字列の長さは、関数 Lengthによって知ることができる。変数 sが文字列型であり、
s:=’abcde’;
と代入が行われたとき、
Length(‘abcde’) および Length(s)
の値はともに5が返される。
ShortString型の下位型として
string[整数値]
がある。ここで、整数値は1以上255以下の値である。
string[255]
は、
ShortString
岡本安晴 2000.12.26;2004.4.20
5 - 39
と同じである。string[整数値]型の変数には(整数値+1)バイトのメモリーが割り当て
られ、0番目の要素に文字列の長さが設定される。文字列は1番目以降の要素に1文字ず
つ格納される。
AnsiString型の変数は、文字列を管理する動的メモリブロックを指し示すポインタであ
り、それ自身は4バイトのメモリーを占めるものである。
文字列同士の演算子には次のものがある。ここで、文字型は長さ1の文字列型とみなす。
=,<>
文字列が同じものである、異なるのものであるかによって
True あるいは False の値をとる
<,>,<=,>=
文字列の順序が辞書式順序によって判定されTrueあるいは
False の値をとる
+
文字列を結合する
ヌルで終わる文字列
Char(0)あるいは#0で表される順序値が 0の文字をヌルという。文字列の終わりをヌルで示
す文字列をヌルで終わる文字列という。ヌルで終わる文字列は、先頭のものを0番目のも
のとして数える。ヌルで終わる文字列は、先頭の0番目の文字を指し示すポインタ型変数
で表す。AnsiChar 型あるいは WideChar 型の文字を指し示すポインタ型として PAnsiChar
あるいは PWideCharがある。AnsiCharと同じ型として Charがあるが、Char型を指し示す
ポインタ型として PCharがある。
Var P : PChar;
と宣言されているとき、
岡本安晴 2000.12.26;2004.4.20
5 - 40
P:=’OK’;
の実行後、P[0]、P[1]、P[2]の各値は
‘O’、’K’、Char(0)
となる。
string型の文字列と PChar型で表されるヌルで終わる文字列は、式や代入において混在
させることができる。
PChar型の変数 Pで表されるヌルで終わる文字列は、型キャスト
string(P)
によって string型の文字列に変換される。string型の変数 sで表される文字列は型キャス
ト
PChar(s)
によってヌルで終わる文字列に変換される。
添字が0から始まる文字型の要素をもつ配列は、PCharあるいは PWideCharと互換性があ
る。例えば、
var s : array[0..10] of Char;
p : PChar;
と宣言されているとき
s:=’sample’;
p:=s;
と代入できる。
岡本安晴 2000.12.26;2004.4.20
5 - 41
5-5 手続き型
手続きや関数を値として扱う、あるいは手続きや関数のパラメタとして用いるときは、手
続き型の宣言を行う。例えば、
function p2( x : extended ) : extended;
begin
p2 := x * x;
end;
あるいは
function p3( x : extended ) : extended;
begin
p3 := x * x * x;
end;
と宣言されているとする。このとき、p2あるいは p3を手続きのパラメタとして用いるとき
は
type tfunc = function( x : extended ) : extended;
のように宣言しておくと、
procedure proc( p : tfunc; var y : extended );
var x : extended;
begin
y:=p(x);
end;
という形で関数をパラメタとして用いることができる。
手続き型は、一般には次のようになる。
岡本安晴 2000.12.26;2004.4.20
5 - 42
手続き型(関数の場合)
function ? 仮パラメータリスト ? : 型名
あるいは
手続き型(手続きの場合)
procedure ? 仮パラメータリスト ? ;
手続き型は、手続きの場合は仮パラメータリストが同じである手続き、関数の場合は仮
パラメータリストが同じであって結果の型も同じ関数を表す(値とする)型である。手続
き型の値、例えば実パラメータとして渡す手続きあるいは関数は、ある手続きあるいは関
数の宣言部ではなく、一番外側で宣言(グローバル宣言)されているものでなければなら
ない。Object Pascal において定義済みの手続きあるいは関数、例えば sinなどは、手続き
型の値とすることはできない。これらの手続きあるいは関数を手続き型の値として用いる
ときは、新たに宣言した手続きあるいは関数の中に埋め込む(ラッパー)。すなわち次のよ
うにする。
Function MySin( x : Extended ) : Extended;
Begin
MySin := sin(x);
End;
上のように宣言した MySinを sinの代わりに用いる。
岡本安晴 2000.12.26;2004.4.20
5 - 43
5-6 バリアント型
場合によっていろいろな型の値をとることができる型として Variant 型がある。この型は
16バイトのレコード型であって、型コード、および型コードで与えられる型の値あるい
は値への参照(ポインタ値)が含まれている。
岡本安晴 2000.12.26;2004.4.20
5 - 44
5-7 型の互換性・同一性
ここでいう「同一性」、「互換性」は、Object Pascal における約束に従ったときの用語であ
る。日常的語感ないしは意味における同一性あるいは互換性とは区別されるべきものであ
る。その意味では、「同一性」あるいは「互換性」という言葉ではなく、他の日常生活での
連想を伴わない記号で表す方が適切かも知れないが、用語に慣れるとこれらの言葉の方が
使いやすい。互換性あるいは同一性は、代入文、あるいは仮パラメータと実パラメータの
関係において取り上げられる。
型の同一性
型が同一であるとは、type文による宣言において等号で関係付けられていることをいう。
例えば、
type reala = extended;
realb = reala;
と宣言されているとき、extended、reala、realbはお互いに同一の型である。
しかし、
type realc = type extended;
と=の後にtypeが置かれているときは、上のrealcはextendedとは同一ではないとされる。
また、次のように type TArray1 = array[1..10] of extended;
TArray2 = array[1..10] of extended;
同じ「array[1..10] of extended」という形であっても、繰り返し用いられたときは同一
でない型として扱われる。すなわち、TArray1と TArray2は同一でない2つの異なった型と
なる(その内容は、どちらも extended 型の要素10個からなる配列である)。同じ型とす
るときは
type TArray1 = array[1..10] of extended;
TArray2 = TArray1;
という形で宣言する。
同様に考えて、
var a : array[1..10] of extended;
岡本安晴 2000.12.26;2004.4.20
5 - 45
b : array[1..10] of extended;
と宣言された aと bは異なる型の配列型変数となるが、
type TArray = array[1..10] of extended;
var c : TArray;
d : TArray;
と宣言された配列型の変数 cと dは同一の型となる。
型の互換性
2つの型の間の互換性はいくつかの場合に分けて明確に定義されているが、直感的には常
識で納得できるものである。互換性のある場合の例をいくつか挙げると以下のようになる。
(1)2つの型が実数型である。例えば、doubleと extended。
(2)2つの型が整数型である。例えば、byteと word。
(3)一方が他方の部分範囲型である。例えば、1..5と Longint。
など。
代入の互換性
「T1:=T2;」という代入文においては、T1と T2の間に代入の互換性の条件が成り立たなけ
ればならない。代入の互換性も常識で納得できるものである。いくつかの例を挙げる。
(1) T1と T2が同一の型である。ただし、これらの型はファイル型あるいはファイル型
を含む構造化型ではないものとする。
(2) T1と T2が互換性のある順序型である。
(3) T1と T2がともに実数型である。
(4) T1が実数型で T2が整数型である。
(5) T1 と T2 がクラス型で、2つが同じ型であるか、あるいは T1 が T2 の祖先である。
このとき、T2には T1のコンポーネントに対応するコンポーネントがすべて含まれ
ている。
など。
クラス型については、第7章「オブジェクトとクラス型」において説明する。
岡本安晴 2000.12.26;2004.4.20
5 - 46
5-8 変数型キャスト
変数は、それ自身の型と同じ大きさの任意の型に読み替える(型キャスト)ことができる。
この変数の型の読み替えを変数型キャストといい、次の形式で行う。
変数型キャスト
型名(変数)
ここで、変数の大きさ(バイト数)と型名の大きさは同じでなければならない。
1バイト整数型として、byte型と ShortInt型がある。byte型の値 255は8ビットすべ
ての値が1である。1バイトの符号付き整数である ShortInt型において、8ビットの値が
すべて1である数は-1を表す。したがって、
var nsb : byte;
sb : ShortInt;
と宣言されているとき、
nsb:=255;
sb:=ShortInt(nsb);
により、sbの値は-1となる。
変数型キャストを利用すると、実数型の内部表現を見ることができる。Extended 型は1
0バイトの大きさを占めるもので、次の形式で表されている。
).(2)1( 16383 fiv es ××−= −
ここで、 sは符号を表す1ビットのおおきさのものであり、0のときは正数、1のときは
負数を表す。eは指数部を表すもので15ビット大きさである。16383を減じた値で2のべ
き乗が求められている。 iと f は仮数部を表すもので、 iは1ビットの大きさであり整数部
を表し、 f は63ビットの大きさで小数部を表す。この全体で10バイトの内容を読み取る
ために次の配列型 trecを用意する。
岡本安晴 2000.12.26;2004.4.20
5 - 47
type trec = record
v : array[1..10] of byte;
end;
すなわち、extended型の変数を trec型に変数型キャストしたものを、バイト単位で内容を
読み取る。
これらの関係を図示すると下図のようになる。
次のプログラムは、上の関係を使って、1.0を extended型で表したときの様子を調べる
ものである。
var x : trec;
e : extended;
s : string;
i : Longint;
という変数宣言のもとで、次のプログラムを実行すると、
e:=1.0;
x:=trec(e);
s:='<1.0> = ';
with x do
for i:=10 downto 1 do
s:=s+' v['+IntToStr(i)+'] = '+IntToStr(v[i]);
ShowMessage(s);
次のメッセージボックスが表示される。
岡本安晴 2000.12.26;2004.4.20
5 - 48
上図より、
0=s
163833$255263 8 ==+×= FFFe
1=i
0=f
となっていることが分かる。このとき、
).(2)1( 16383 fiv es ××−= −
)0.1(2)1( 16383163830 ××−= −
0.1=
となり、確かに 1.0が表されていることを確認することができる。
サンプルプログラム PSample5e1.dprは上の2つの変数型キャストを実行するものである。
岡本安晴 2000.12.26;2004.4.20
5 - 49
参考文献 (1) Delphi5オンラインヘルプ、Inprise Corporation, 1999. (2) Delphi5 Object Pascal言語ガイド、インプライズ株式会社、1999. (3) 岡本安晴「Delphiプログラミング入門」、CQ出版社、1997.