Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2011年05月12日] [更新 2019年11月26日] Delphi 一般・その他
メモリと文字列バッファ確保の関数
動作確認等 -
[整理番号 - 0720]







01_メモリと文字列バッファ確保の関数


バッファとしてのメモリや文字列(要素数)を確保,割り当てるために使用する関数類です.
Delphi 2009 から Unicode 対応になりましたが,2011年5月12日現在,当サイトのサンプルの文字列バッファの確保のコードには,Delphi 2009 以降では正常動作しないものがあります.必要があればこの表を参考にして修正してください.

この他,項目識別子には特別なメモリ確保と解放の方法があります.
また,関数や手続きではありませんが,変数を array [0..Count -1] of Char という配列型で定義すれば,Count 分の文字領域を確保します.これを Char 型の静的配列と言います.



表1
メモリと文字列バッファ確保関係の関数

関数・手続き 説明
GetMem GetMem(p, Size);
Size バイトのメモリブロックを割り当て,このメモリのアドレスを p に返す
バッファの初期化はしない
バッファは FreeMem 手続きで破棄する
AllocMem p := AllocMem(Size);
指定 Size バイトのメモリブロックを割り当て,このメモリのアドレスを返す
内部で GetMem を呼んで,更に 0 に初期化する
バッファは FreeMem 手続きで破棄する
GetMemory GetMemory(p, Size);
GetMem 関数と同じ.GetMem の C++ 互換関数
バッファは FreeMemory 関数 (FreeMem の C++ 互換関数) で破棄する
ReallocMem ReallocMem(p, Size);
メモリブロックの再割り当てに使用する
p は既存メモリブロックのポインタ.Size は変更後のバイト数
ReallocMemory ReallocMem 関数の C++ 互換関数
SetLength

SetLength(S, NewLength);
文字列変数 (String) または動的配列変数の長さを設定または再設定する
文字列の場合は文字数 (エレメント数) .動的配列の場合は要素数の設定
オンラインヘルプには,文字数が 255 までとの記述があるが,その制限はない
設定した長さは Length 関数で取得できる

SetLength 手続きで長さを設定した場合,文字列の要素は未定義状態となる.動的配列の要素は 0 または nil に初期化される
長さを再設定した場合,文字列も動的配列も既存要素はそのままだが,文字列の追加分は未定義状態となり,動的配列の追加分は 0 または nil に初期化される

StrAlloc PChar := StrAlloc(Size);
Size - 1 文字分のヌルで終わる文字列用にバッファを確保して、メモリの先頭文字を指すポインタを返す
確保可能な文字数は StrBufSize 関数で取得できる
バッファは StrDispose 手続きで破棄する
※ オンラインヘルプではバイト数になっているが実際は文字数
StrNew PChar := StrNew(PChar);
PChar (PChar 型の変数) のコピーを作成し,StrAlloc 関数を呼び出して領域を確保して文字列を指すポインタを返す.
バッファは StrDispose 手続きで破棄する
New New(var X: Pointer);
引数で指定した動的変数を新しく作成する.P は任意のポインタ型変数
割り当てられるメモリサイズは P が示す型のサイズ.
割り当てたメモリは Dispose 手続きで解放する
GlobalAlloc hWnd := GlobalAlloc(uFlags, dwBytes);
属性を指定して指定バイト数のバッファを確保し,メモリオブジェクトのハンドルを返す
実際に割り当てられているバイト数は GlobalSize 関数を使用する
GlobalFree 関数を使用してメモリを解放する
ZeroMemory メモリの初期化用の手続き
指定メモリブロックに値 0 を書き込む
FillMemory メモリの初期化用の手続き
指定メモリブロック全体に指定したバイト値を書き込む
FillChar 指定サイズのバイト領域に,指定したバイト値を書き込む
主な用途は,Char 型の静的配列の初期化


関連する関数に以下のようなものもあります.


表2

関数・手続き 説明
GlobalLock p := GlobalLock(hBuf);
グローバルメモリオブジェクトをロックし,メモリブロックの最初の 1 バイトのポインタを返す
GlobalUnlock 関数でロックを解除する




02_FillChar 手続きによる初期化


FillChar 手続きは,主に Char 型の静的配列の初期化,つまり,全ての要素の値を Null 文字にするために使用します.初期化のサイズは,変数または型が占めるバイト数を返す SizeOf 関数を使用するのが一般的です.

バッファとして Char 型の静的文字配列を使用するコードを実行する場合,使用していない,あるいは使用しなくなったメモリの値が 0 に初期化されているわけではありません.そのため,バッファとしての文字列に,いわゆる「ゴミ」が入ってしまうことがあります.FillChar 手続きによる初期化を実行するようにした方が確実であることは間違いありません.



[備考]
同じバイト値ではなく,同じ「文字」だけの文字列の作成には StringOfChar 関数が使用できます.同じ「文字列」を指定回数繰り返す文字列の作成には DupeString 関数が使用できます.
DupeString 関数を使用するには,uses に System.StrUtils が必要です.





03_メモリを確保した変数の扱い


確保したメモリは解放処理が必要です.そうしないとメモリリークが発生します.
ただし,SetLength 手続きで確保したメモリ領域は,スコープを抜けると指導的に開放,破棄される仕組みになっています.

メモリを確保したポインタ型の変数には @ 演算子付きの変数やポインタ型の変数を代入しないようにします.そのような代入処理を行うと,データの格納アドレスが変わってしまい,正常な処理が期待できなくなります.その状態では確保したメモリが解放できなくなり「無効なポインタ操作」のエラーが発生します.

PChar 型や String 型の文字列用のメモリを確保した場合も同じですが,メモリを SetLength 手続きで確保した場合は「無効なポインタ操作」のエラーは発生しません.
確保したメモリに文字列を書き込むには,代入ではなく,StrPCopy, StrPLCopy 関数や MoveChars 手続き等を使用します.インデックス (添字) を使用した文字単位の代入ば問題ありません.PChar 型の変数ではポインタの移動やポインタ演算による文字の代入も可能です.

メモリを確保するということは,データの格納場所を,固定したアドレスの領域 (メモリブロック) にすることです.そのアドレスが変わってしまってはメモリ確保の意味がありません.

メモリを解放した後であれば,それらの変数に値を直接代入しても構いません.