[ クラス | フォーム | 配列、動的配列 | 文字列 | Delphi の格言 | IDE の小技 | エッセンス ]
array[0..255] of Char;
の意味がいまいちわかりません。静的配列、と言うらしいのですが、どのような構造になっているのでしょうか。
単純に Char 型がメモリ上に 256個隙間なくならんでいるだけの構造です。Char は1バイトですから、array[0..255] of Char 型は 256バイトのメモリ領域を占めます。静的配列の 「静的」の意味は要素(この場合 Char 型) の個数が決まっていて変更できないという意味です。[delphi-ml:32706]
string の配列を返すように変更したいと思っています。その場合の関数の宣言の仕方なのですが、戻り値を配列にするにはどのように宣言すればよいのでしょうか?
function Test: array of string
では駄目でした。この場合は下のように予め string の配列型を宣言しておくしかないのでしょうか?
type TStringArray: array of string; function Test: TStringArray;
そうです。Pascal は型に厳しい言語なので、まったく同じ内容であっても、違う場所で宣言された型は互換性がなくなります。
ObjectPascal 言語ガイドの「データ型, 変数, 定数」のあたりのトピックスを一通り眺めると、だいたいわかるかと思います。(特に 代入互換性、型の互換性、など)
Delphi 6 なら、定義内容は同じですが Types unit に TStringDynArray があります。こちらを使う方が「型が違う」とか避けやすいでしょう。[delphi-ml:63827]
例えば、配列B(静的・動的に限らず)を手続きに渡すとき、
procedure F( var A : array of Byte ) ;
のような手続きに対して、
F( B[0] ) ; // F( B ) でも同じ F( B[1] ) ; F( B[2] ) ;
等のように、配列の途中から渡せる。つまり、配列を参照渡しするときは、配列の途中から渡せる。
procedure F( A : array of Byte ) ;
もしくは、
procedure F( const A : array of Byte ) ;
のような手続きに対して、
F( B ) ;
のみで、配列の先頭からしか渡せない。つまり、配列を値渡しするときは、配列の先頭からしか渡せない。
これで、あってるでしょうか?また、配列を値渡しするときは、手続きFの宣言を変更せず、配列の途中から渡す方法はあるのでしょうか?
えーと、できてしまうみたいですが(^^; 文法上は配列を途中から渡すことはできません。オープン配列パラメータに要素型の値や変数(B[1} 等)を渡すということは、要素数が1の配列を渡すことになるとヘルプに書いてあります。
ですから、現在は途中から渡すように書くことができますが、文法の範囲外のやりかたなので、将来の保証はないと思います。
また、渡された配列の添え字の範囲は Low, High 関数で確認できます。{$RANGECHECKS ON} でコンパイルすれば添え字がどの範囲で正当なのかチェックできます。B[1] を渡すと、A の添え字の範囲は 0..0 で A[1} は実行時に範囲チェックエラーになります。
関数などへの var や const での渡し方については、以下のようになってるみたいです。
基本的に、var は、関数・手続きに渡されたデータの変更結果が呼出元へも反映されます。(最終的にはポインタ渡ししてるのと同じなのかな?)const を付けると、渡されたデータは、関数・手続き内部で変更することができません。(もちろん例外はありますが ^^;)何も付けない場合、その中間で、関数・手続き内部で変更は可能ですが、呼出元へ、その結果が反映されることはありません。
ということで、const などを付けても基本的には渡せるデータが限定されるということはないみたいですね。var の場合は結果が返るため、定数は渡せないみたいです。[delphi-ml:51825]
ローカル配列に、宣言時、または宣言後にまとめて値を代入するにはうしたら良いんでしょうか?
C言語だったら
float locate[3] = { 10.0, 50.0, 20.0 };
VBだったら
Dim intArray() as integer intArray = Array(1,2,5)
ヘルプで配列型定数を引きましょう(^^
尚、ローカル配列と配列型定数の型が「同じ」でないと代入互換性はありません。別々に宣言された配列型は宣言が全く同じでも Pascal では別の型として扱われることに注意してください。type でまず配列型の型名を宣言し、その型名を使って、配列型定数とローカル配列を宣言する必要があります。[delphi-ml:50643]
Delphiでは、こんな感じです。
array[1..3] of Double = (10.0, 50.0, 20.0); type TStrArray = array[0..2] of string; procedure TForm1.Button1Click(Sender: TObject); var str1: array[0..2] of string; str2: array[0..2] of string; begin str1 := str2; // エラー!互換性のない型です。 end; procedure TForm1.Button2Click(Sender: TObject); var str1, str2: array[0..2] of string; begin str1 := str2; // OK end; procedure TForm1.Button3Click(Sender: TObject); var str1: TStrArray; str2: TStrArray; begin str1 := str2; // OK end;
Delphi では、要素数が未定のまま、配列型を宣言する事はできないのでしょうか。 Cではできたような気がするのですが…
仮パラメータとしてならば「オープン配列パラメータ」が使えます。[delphi-ml:13476]
procedure MyProc(arr: array of string); // オープン配列パラメータ var i: Integer; begin for i := Low(arr) to High(arr) do Form1.ListBox1.Items.Add(arr[i]); end; procedure TForm1.Button1Click(Sender: TObject); var StrArr1: array of string; StrArr2: array[0..1] of string; i: Integer; begin SetLength(StrArr1, 3); for i := Low(StrArr1) to High(StrArr1) do StrArr1[i] := 'Hello'+IntToStr(i); StrArr2[0] := 'Hello'; StrArr2[1] := 'World'; MyProc(StrArr1); // 要素数 3 の動的配列を渡します。 MyProc(StrArr2); // 要素数 2 の配列を渡します。 end;
+-------------------+ |動的配列(ポインタ) | <-- SizeOf はこれの大きさを返す +-------------------+ | +------------------+(このポインタは最初の要素を指す) | V 動的配列が動的に確保したメモリ +-------------+----------+-----+-----+- +--------+ |参照カウント |要素数(n) |要素0|要素1| ・・・・| 要素n-1| +-------------+----------+-----+-----+- +--------+
という構造になってて、Length が返すのは上図の 要素数(n) です。
SizeOf は型のコンパイル時の静的な大きさを求める擬似関数なので、動的配列の動的な大きさを求めるような用途には使えません。SizeOf(動的配列名)はポインタの大きさ4バイトを返すだけです。[delphi-ml:60567]
ヘルプによれば、「動的配列を切り捨てるには,Copy 関数に配列変数を渡し,結果を配列変数に代入します。たとえば,A が動的配列である場合,A := Copy(A, 0, 20) は A の 21 番目以降の要素を切り捨てます。」とのことですが、これを
SetLength(A, 20);
と書いてはいけないのでしょうか。
SetLength で大丈夫です。SetLength はメモリを再確保します。また切り捨てられた要素の Finalize や増えた要素の Initialize もきちんとやってくれます。ですから配列要素に文字列やインターフェースを使っても大丈夫です(^^ [delphi-ml:57158]
procedure TForm1.Button1Click(Sender: TObject); var DynamicArr: array of string; i: Integer; begin SetLength(DynamicArr, 20); for i := 0 to High(DynamicArr) do DynamicArr[i] := IntToStr(i); DynamicArr := Copy(DynamicArr, 0, 10); // 11 番目以降を切り詰めます for i := 0 to High(DynamicArr) do ListBox1.Items.Add(DynamicArr[i]); end; procedure TForm1.Button2Click(Sender: TObject); var DynamicArr: array of string; i: Integer; begin SetLength(DynamicArr, 20); for i := 0 to High(DynamicArr) do DynamicArr[i] := IntToStr(i); SetLength(DynamicArr, 10); for i := 0 to High(DynamicArr) do ListBox1.Items.Add(DynamicArr[i]); end;
procedure a var buf: array of byte; begin end;
で記述されるような動的配列は使い終わったら Dispose 等で開放しなくて
はならないのでしょうか。それとも必要がなくなれば自動的に開放されるのでしょうか。
動的配列の明示的な解放は、基本的には不要です。
デバッガのCPUウィンドウで見ると分かりますが、スコープから出たときにcall @DynArrayClearが自動的に実行され、参照カウントの処理とメモリの解放が行われます。
もちろんコンパイラ任せにせず明示的に解放するのも良い習慣だと思います。またスコープが広い動的配列では、明示的に解放することで不要なメモリを速やかに解放できるという利点もあります。
いろいろ試してみた結果、AnsiString とよく似ているようです。
というわけで GetMem でレコードを取得するとき以外は 従来の固定長配列とほぼ同じ取り扱いで よいようです。[delphi-ml:35172]
[ クラス | フォーム | 配列、動的配列 | 文字列 | Delphi の格言 | IDE の小技 | エッセンス ]
更新日:2005-02-12