Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2017年05月14日] [更新 2022年08月11日] Delphi 一般・その他
   
多次元の静的配列と動的配列
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
後半は,Windows 10 ビルド 19043 + Delphi XE5(UP2) Pro VCL-64
[整理番号 - 0690]







Delphi の 多次元配列


多次元配列を扱う際の参考コードです.
グローバル変数の配列は要素が初期化されますが,ローカル変数の場合は不定値になります.
本ページのサンプルでは整数型の配列を扱っていますが,他の変数型の配列も同様に扱えます.

2 次元の配列は,数値計算における行列・マトリックスに相当します.



[備考 1]
静的配列と動的配列は,配列データの格納構造が全く違います.要素単位での操作であれば問題ありませんが,配列変数を相互に代入することはできません.

[備考 2]
要素数の多い配列を扱う場合,ローカル変数だとメモリの確保に失敗して,実行時にメモリアクセス違反の例外が発生することがあります.ローカル変数はヒープ領域を使用します.この現象は,ヒープ領域のメモリが不足することによって発生します.
その場合は配列変数をグローバル変数にします.グローバル変数は,アプリケーションのグローバルなメモリを使用します.





01_2 次元の静的配列


2 次元の静的配列の例です.
静的配列は,配列用の変数を定義する時にその要素数を設定する配列のことです.
array [..., ..] of という形式で定義します.[ ] 内を , (カンマ) で区切れば何次元の配列でも定義できます.



図1
実行結果 (2 x 3 の行列)

リスト1
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Charset := ANSI_CHARSET;
  Memo1.Font.Name    := 'Courier New';
  Memo1.Font.Size    := 11;
end;

//=============================================================================
//  多次元配列
//  2次元の静的配列
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArrayData : array [0..1, 0..2] of Integer;
  LText : string;
  i     : Integer;
  j     : Integer;
begin
  Memo1.Lines.Clear;


  //配列の各要素に値を代入
  for i := 0 to Length(LArrayData) - 1 do begin
    for j := 0 to Length(LArrayData[i]) - 1 do begin
      LArrayData[i][j] := (i + 1) * 10 + (j + 1);
    end;
  end;

  //1行ごとに書式を設定して値を表示
  for i := 0 to Length(LArrayData) - 1 do begin
    LText := '';
    for j := 0 to Length(LArrayData[i]) - 1 do begin
      LText := LText + Format('%6d', [LArrayData[i][j]]);
    end;
    Memo1.Lines.Add(LText);
  end;
end;

end.


上のコードでは,配列要素を 0 から開始していますが,0 よりも大きい値を開始要素とするときは以下のように,Low, High 関数で開始要素番号と終了の要素番号を取得することができます.
配列の要素を操作する場合は,Low 関数と High 関数を使用した方が無難です.



  for i := Low(LArrayData) to High(LArrayData) do begin
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LArrayData[i][j] := (i + 1) * 10 + (j + 1);
    end;
  end;




02_配列要素は [ I ] [ J ] と [ I, J ] のどちらでも参照可能


上のとサンプルコードでは,配列要素の参照に [i][j] という形式を使用していますが,一般的に使用されている[i,j] という形式でも配列要素を参照することができます.





03_2 次元のネスト形式の静的配列


静的な配列の定義は,以下のように,配列の配列というようなネストした形式でも定義できます.
動的な多次元配列の場合は常にこの形式で定義します.



図2
実行結果 (4 x 3 の行列)

リスト2
//=============================================================================
//  多次元配列
//  2次元のネストした静的配列
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArrayData : array [0..3] of array [0..2] of integer;
  LText : string;
  i     : Integer;
  j     : Integer;
begin
  Memo1.Lines.Clear;


  //配列の各要素に値を代入
  for i := Low(LArrayData) to High(LArrayData) do begin
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LArrayData[i, j] := (i + 1) * 10 + (j + 1);
    end;
  end;

  //1行ごとに書式を設定して値を表示
  for i := Low(LArrayData) to High(LArrayData) do begin
    LText := '';
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LText := LText + Format('%6d', [LArrayData[i, j]]);
    end;
    Memo1.Lines.Add(LText);
  end;
end;




04_2 次元の動的配列


2 次元の動的配列の例です.動的な多次元配列は配列の配列というネスト形式で定義します.
SetLength で配列の要素数を設定します.
以下のコードでは 2 x 3 の配列を設定しています.

Types ユニットにはいくつか動的配列の型定義があります.それを使用すると,本サンプルの配列変数は次のようにも書くこともできます.


  LArrayData : array of TIntegerDynArray;


図3
実行結果

リスト3
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Charset := ANSI_CHARSET;
  Memo1.Font.Name    := 'Courier New';
  Memo1.Font.Size    := 11;
end;

//=============================================================================
//  多次元配列
//  2次元の動的配列
//  型なし
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArrayData : array of array of Integer;
  LText : string;
  i     : Integer;
  j     : Integer;
begin
  Memo1.Lines.Clear;

  //配列の要素数を設定
  //2 x 3 の配列(行列)とないる
  SetLength(LArrayData, 2, 3);


  //配列の各要素に値を代入
  for i := Low(LArrayData) to High(LArrayData) do begin
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LArrayData[i, j] := (i + 1) * 10 + (j + 1);
    end;
  end;

  //1行ごとに書式を設定して値を表示
  for i := Low(LArrayData) to High(LArrayData) do begin
    LText := '';
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LText := LText + Format('%8d', [LArrayData[i, j]
      ]);
    end;
    Memo1.Lines.Add(LText);
  end;
end;

end.


以下のように,最初の次元の要素数を設定したら,その次元の各要素ごとに要素数を設定していく方法もあます.この方法では,最初の次元の各要素に対応した配列の要素数を個別に設定できます.



リスト4
  //最初の次元の要素数
  SetLength(LArrayData, 2);

  //次の次元の要素数を設定
  //各行ごとに行の要素数を設定可能
  for i := Low(LArrayData) to High(LArrayData) do begin
    SetLength(LArrayData[i], 3);
  end;




05_3 次元の動的配列


3 次元の動的配列の場合も配列の配列というネスト形式で定義します.
SetLength で配列の要素数を設定します.
以下のコードでは 2 x 3 x 4 の配列を設定しています.



図4
実行結果
  • 最初の次元の要素数は 2
  • その各々の要素に 3 x 4 の行列が定義されている

リスト5
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Charset := ANSI_CHARSET;
  Memo1.Font.Name    := 'Courier New';
  Memo1.Font.Size    := 11;
end;

//=============================================================================
//  多次元配列
//  3次元の動的配列
//  型なし
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArrayData : array of array of array of Integer;
  LLen  : Integer;
  LText : string;
  i     : Integer;
  j     : Integer;
  k     : Integer;
begin
  Memo1.Lines.Clear;


  //配列の要素数の要素数
  //2 x 3 x 4 の配列となる
  SetLength(LArrayData, 2, 3, 4);


  //配列の各要素に値を代入
  for i := Low(LArrayData) to High(LArrayData) do begin
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      for k := Low(LArrayData[i][j]) to High(LArrayData[i][j]) do begin
        LArrayData[i][j][k] := (i + 1) * 100 + (j + 1) *10 + k + 1;
      end;
    end;
  end;

  //1行ごとに書式を設定して値を表示
  for i := Low(LArrayData) to High(LArrayData) do begin
    Memo1.Lines.Add('   i=' + IntToStr(i));
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LText := '';
      for k := Low(LArrayData[i][j]) to High(LArrayData[i][j]) do begin
        LText := LText + Format('%8d', [LArrayData[i, j, k]]);
      end;
      Memo1.Lines.Add('j=' + IntTostr(j) + LText);
    end;
    Memo1.Lines.Add('');
  end;
end;

end.


2 次元の動的配列と同じようにループ処理で配列要素数を設定することもできます.



リスト6
  //最初の次元の要素数
  SetLength(LArrayData, 2);

  //次の次元の要素数を設定
  //各行ごとに行の要素数を設定可能
  for i := Low(LArrayData) to High(LArrayData) do begin
    SetLength(LArrayData[i], 3);
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      SetLength(LArrayData[i][j], 4);
    end;
  end;




06_型定義の多次元の動的配列要素の初期化と配列の初期化


型宣言の動的配列であれば,InitializeArray 関数を使用して,その要素の値を初期化できます.ただし,FillChar とは違い,特定の値にはできません.デフォルトの初期値となります.
下に示すコードは,TIntegerDynArray 型の整数配列の各要素の値を全て 0 にする例です.初期化のコードは各次元ごとに実行する必要があります.

型宣言した配列でなくても,動的配列であれば,Finalize 関数を使用して配列を初期化できます.配列を初期化すると,配列の要素数は 0 になります.



図5
動的配列の要素の初期化
  • 各要素に値を代入した後,初期化したので全ての要素の値が 0 になっている

リスト7
//=============================================================================
//  多次元配列
//  2次元の動的配列
//  型定義の動的配列要素の初期化
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
type
  TMyDnyArray = array of array of Integer;
var
  LArrayData : TMyDnyArray;
  LText : string;
  i     : Integer;
  j     : Integer;
begin
  Memo1.Lines.Clear;


  //配列の要素数を設定
  SetLength(LArrayData, 2, 3);


  //配列の各要素に値を代入
  for i := Low(LArrayData) to High(LArrayData) do begin
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LArrayData[i, j] := (i + 1) * 10 + (j + 1);
    end;
  end;

  //各次元の配列ごとに要素の値を初期化する
  for i := Low(LArrayData) to High(LArrayData) do begin
    InitializeArray(LArrayData[i], TypeInfo(TMyDnyArray), Length(LArrayData[i]));
  end;

  //1行ごとに書式を設定して値を表示
  for i := Low(LArrayData) to High(LArrayData) do begin
    LText := '';
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LText := LText + Format('%8d', [LArrayData[i, j]
      ]);
    end;
    Memo1.Lines.Add(LText);
  end;
end;


以下のように ZeroMemory 関数でも 0 に初期化できます.


リスト8
  //各次元の配列ごとに要素の値を初期化する
  for i := Low(LArrayData) to High(LArrayData) do begin
    ZeroMemory(LArrayData[i], Length(LArrayData[i]) * SizeOf(Integer));
  end;




07_関数の引数等で使用するには型定義の配列が便利


グローバルで使用する多次元配列は型定義して使用すると便利です.特に関数やプロシージャーの引数に配列を渡す場合は特にそうです.
以下のサンプルでは次のように型定義しています.


type
  TMyArray = array [0..1, 0..2] of Integer;


型宣言した型定義の配列を使用すると便利ですが,関数の引数にその型を指定すると,それは Delphi 専用の関数となります.DLL に組み込む等,汎用的な関数にするに工夫が必要です.以下の記事を参考にしてください.
DLL に組み込んだ関数を使用する場合は,型定義の配列変数を引数に使用することができます.




図6
実行結果

リスト9
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  PMyArray = ^TMyArray;
  TMyArray = array [0..1, 0..2] of Integer;

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//-----------------------------------------------------------------------------
//  多次元配列をプロシージャや関数の引数にする
//  型定義をした配列変数を使用する例
//-----------------------------------------------------------------------------
procedure SetArrayElement(var AArray: TMyArray);
var
  i : Integer;
  j : Integer;
begin
  //配列の各要素に値を代入
  for i := Low(AArray) to High(AArray) do begin
    for j := Low(AArray[i]) to High(AArray[i]) do begin
      AArray[i][j] := (i + 1) * 10 + (j + 1);
    end;
  end;
end;

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Charset := ANSI_CHARSET;
  Memo1.Font.Name    := 'Courier New';
  Memo1.Font.Size    := 11;
end;

//=============================================================================
//  型定義した多次元配列を使用する例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArrayData : TMyArray;
  LText : string;
  i     : Integer;
  j     : Integer;
begin
  Memo1.Lines.Clear;


  //配列の各要素に値を代入
  SetArrayElement(LArrayData);

  //1行ごとに書式を設定して値を表示
  for i := Low(LArrayData) to High(LArrayData) do begin
    LText := '';
    for j := Low(LArrayData[i]) to High(LArrayData[i]) do begin
      LText := LText + Format('%6d', [LArrayData[i][j]]);
    end;
    Memo1.Lines.Add(LText);
  end;
end;

end.




08_設定可能な配列のサイズ


静的配列で設定可能なサイズは,Windows では,32 ビット,64 ビットの EXE に関係なく 2 GB までです.32 ビットの EXE のプロセスで使用可能なメモリは 2 GB です.そのため,実際に設定可能なサイズはそれよりも少なくなります.

動的配列の場合,32 ビットの EXE では,設定可能なサイズは 2 GB までです.
64 ビットの EXE では,動的配列は,プロセスで使用可能なメモリサイズを超えるサイズを設定できます.上限は,システムで使用可能なメモリサイズです.物理メモリと仮想メモリを合わせたメモリが,システムで使用可能なメモリです.配列やリスト関係のオブジェクトは,作業用に多くのメモリを使用します.したがって,実際には,それより少ないサイズになります.
必要であれば,仮想メモリのサイズは変更できます.

下図は,Double 型の 3 次元の動的配列のメモリ確保例です.多くの仮想メモリを使用しています.仮想メモリはハードディスク (HDD) にあります.そのため,メモリ確保の処理には時間がかかっています.
SSD (Solid State Drive - ソリッド・ステート・ドライブ) であれば高速化が期待できます.




図7
動的配列のメモリ確保例
  • EXE は 64 ビット
  • 最初の値は,配列要要素全体のサイズ
  • 2 番目の値が確保に使用した時間
  • 3 番目の値は,全ての配列要素に値を代入するために要した時間

下図は,今回のテストで使用したメモリ量を示しています.
テストプログラムを終了後にこれらのメモリが解放されます.サンプルを IDE から実行した場合,フォームを閉じても,これらのメモリが解放されるまで IDE は元の状態に戻りません.




図8
メモリ使用量
  • [473_メモリの使用状況を表示] のサンプルを使用
  • Windows 10 対応のために,アプリケーション・マニュフェストを使用している

リスト10
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;
var
  Form1: TForm1;

implementation

uses
  Winapi.MMSystem;

{$R *.dfm}

type
  T3DDynArray = array of array of array of Double;
var
  FArray1 : T3DDynArray;

//=============================================================================
//  動的配列のメモリ確保
//  プロセスで使用可能なメモリサイズを超える配列の例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LTime  : Cardinal;
  LSize  : NativeInt;
  LBytes : UInt64;
begin
  Screen.Cursor := crHourGlass;
  Memo1.Lines.Clear;
  Memo1.Update;

  LTime := timeGetTime;

  LSize  := 2048;
  LBytes := LSize * LSize * LSize * SizeOf(Double);
  Memo1.Lines.Add(FormatFloat(',0 KB', LBytes / 1000));

  SetLength(FArray1, LSize, LSize, LSize);

  Memo1.Lines.Add(FormatFloat('#0.000 sec', (timeGetTime - LTime)  / 1000));
  Memo1.Lines.Add('End');
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  動的配列の要素に数値を代入
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LTime : Cardinal;
  I     : NativeInt;
  J     : NativeInt;
  K     : NativeInt;
begin
  Screen.Cursor := crHourGlass;
  LTime := timeGetTime;

  for I := 0 to Length(FArray1) - 1 do begin
    for J := 0 to Length(FArray1[I]) - 1 do begin
      for K := 0 to Length(FArray1[I][J]) - 1 do begin
        FArray1[I][J][K] := 1.0;
      end;
    end;
  end;

  Memo1.Lines.Add(FormatFloat('#0.000 sec', (timeGetTime - LTime)  / 1000));
  Memo1.Lines.Add('End');
  Screen.Cursor := crDefault;
end;

end.


[備考]
科学技術計算では 3 次元空間のデータを扱うことがあります.例えば,気象データは,位置として,緯度,経度,高度を使用する 3 次元データです. このようなデータ処理にはスーパーコンピュータが使われています.スーパーコンピュータ 富岳の場合,そのメモリは 4.85 PiB ( ペビバイト ) です.
1 PiB は 2 の 50 乗 バイト ( = 1,125,899,906,842,624 バイト ) です





09_配列の要素数と配列のサイズ ( バイト数 ) の取得


配列の各次元の要素数は,要素数を指定する定数,あるいは変数の値で知ることができますが,Length 関数で取得することもできます.要素の全数は,それらの値を乗算すれば取得できます.また,要素のバイト数は,任意の要素を引数にして SizeOf 関数で取得できます.
次のコードは,動的配列における取得例です.




図9
動的配列の各次元の要素数を Length 関数で取得

リスト11
//=============================================================================
//  動的配列の各次元の要素数
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArray : array of array of array of Double;
  LCount : UInt64;
  LBytes : Integer;
begin
  Memo1.Lines.Clear;

  SetLength(LArray, 120, 50, 200);

  // 各次元の要素数
  Memo1.Lines.Add(IntToStr(Length(LArray)));
  Memo1.Lines.Add(IntToStr(Length(LArray[0])));
  Memo1.Lines.Add(IntToStr(Length(LArray[0][0])));


  // 全要素数
  LCount := Length(LArray) * Length(LArray[0]) * Length(LArray[0][0]);

  // 要素のバイト数.SizeOf(Double) と同じ値 )
  LBytes := SizeOf(LArray[0][0][0]);

  // 全バイト数
  LBytes := LBytes * LCount;


  Memo1.Lines.Add(FormatFloat(',0', LCount) + ' elements');
  Memo1.Lines.Add(FormatFloat(',0', LBytes) + ' byte');
end;


次のサンプルも Length 関数を使用しますが,次元ごとにループ処理で加算する方法です.
例えば 2 次元配列の場合,行ごとに列数 (要素数) を設定できます.そのような配列では,この方法でないと正しい要素数は取得できません.
行単位で何かしらの処理が必要な場合も,この方式を使用することになります.




図10
配列の要素数を Length 関数で取得
  • ループで各次元ごとに処理する例
  • 2 次元の動的配列の場合

図11
配列の要素数を Length 関数で取得
  • ループで各次元ごとに処理する例
  • 3 次元の動的配列の場合

リスト12
//=============================================================================
//  2 次元の動的配列の全要素数
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArray : array of array of Double;
  I      : NativeInt;
  LCount : NativeInt;
  LBytes : UInt64;
begin
  Memo1.Lines.Clear;

  SetLength(LArray, 50, 100);
  // 30 行目だけ要素数を変えてみる ( 0 ベースなので要素番号は 29)
  SetLength(LArray[29], 50);

  // 全次元の要素数とバイト数
  LCount := 0;
  LBytes := 0;
  for I := 0 to Length(LArray) - 1 do begin
    LCount := LCount + Length(LArray[I]);
    LBytes := LBytes + Length(LArray[I]) * SizeOf(Double);
  end;

  Memo1.Lines.Add(FormatFloat(',0', LCount) + ' elements');
  Memo1.Lines.Add(FormatFloat(',0', LBytes) + ' byte');
end;

//=============================================================================
//  3 次元の動的配列の全要素数
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LArray : array of array of array of Double;
  I      : NativeInt;
  J      : NativeInt;
  LCount : NativeInt;
  LBytes : UInt64;
begin
  Memo1.Lines.Clear;

  SetLength(LArray, 100, 100, 100);

  // 配列の全要素数と全バイト数
  LCount := 0;
  LBytes := 0;
  for I := 0 to Length(LArray) - 1 do begin
    for J := 0 to Length(LArray[I]) - 1 do begin
      LCount := LCount + Length(LArray[I][J]);
      LBytes := LBytes + Length(LArray[I][J]) * SizeOf(Double);
    end;
  end;

  // LCount は Length(LArray) * Length(LArray[0]) * Length(LArray[0][0]) と同じ値
  // LBytes は LCount * SizeOf(Double) と同じ値

  Memo1.Lines.Add(FormatFloat(',0', LCount) + ' elements');
  Memo1.Lines.Add(FormatFloat(',0', LBytes) + ' bytes');
end;


静的配列も,Length 関数で各次元の要素数を取得できます.
静的配列の場合,SizeOf 関数の引数を配列名にすると,配列の全バイトが取得できます.動的配列の変数はポインタです.動的の配列の場合は,SizeOf 関数の戻り値はポインタのサイズ (バイト数) です.




図12
各次元の要素数を Length 関数で取得
  • 3 次元の静的配列の場合

リスト13
  //=============================================================================
//  静的配列の各次元の要素数と総バイト数
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArray : array [3..102, 2..21] of array [0..49] of Double;
  LBytes : UInt64;
  LCount : UInt64;
begin
  Memo1.Lines.Clear;

  // 各次元の要素数
  Memo1.Lines.Add(IntToStr(Length(LArray)));
  Memo1.Lines.Add(IntToStr(Length(LArray[3])));
  Memo1.Lines.Add(IntToStr(Length(LArray[3][2])));

  // 配列の全バイト数と全要素数
  LBytes := SizeOf(LArray);
  LCount := LBytes div SizeOf(Double);

  // LCount の値は LBytes div SizeOf(LArray[3][2][0]) と同じ値

  Memo1.Lines.Add(FormatFloat(',0', LCount) + ' elements');
  Memo1.Lines.Add(FormatFloat(',0', LBytes) + ' byte');
end;


静的配列でループ処理を行う時は,以下のように Low 関数,High 関数を使用するようにします.そうしないと,配列要素の処理の際にアクセス違反の例外が発生する原因となります.
このサンプルのような,他のオブジェクトに対する処理がない単純なコードだけのプログラムの吐合は,例外が発生することはまずありません.




図13
静的配列のループ処理には,Low 関数, High 関数を使用する

リスト14
  //=============================================================================
//  静的配列の各次元の全要素数と総バイト数
//  静的配列のループ処理には Low 関数と High 関数を使用する
//
//  Low 関数は要素番号の下限値の取得
//  High 関数は要素番号の上限値の取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LArray : array [3..102, 2..21] of array [0..49] of Double;
  LBytes : UInt64;
  LCount : UInt64;
  I      : NativeInt;
  J      : NativeInt;
begin
  Memo1.Lines.Clear;

  // 配列の全要素数と全バイト数
  LCount := 0;
  LBytes := 0;
  for I := Low(LArray) to High(LArray) do begin
    for J := Low(LArray[I]) to High(LArray[I]) do begin
      LCount := LCount + Length(LArray[I][J]);
      LBytes := LBytes + Length(LArray[I][J]) * SizeOf(Double);
    end;
  end;

  Memo1.Lines.Add(FormatFloat(',0', LCount) + ' elements');
  Memo1.Lines.Add(FormatFloat(',0', LBytes) + ' bytes');
end;