6

シンプルなレコードタイプがあります。このレコードの新しいインスタンスを割り当て、プロシージャ( "_clone")を使用して、既存のレコードから新しいレコードに値をコピーします。文字列値を割り当てた場合にのみアクセス違反が発生します。

何か案は?ヘルプは大歓迎です。


タイプ定義:

TPointer = ^TAccessoryItem;
TAccessoryItem = Record
  Id : Integer;
  PartNumber : String;
  Qty : Integer;
  Description : String;
  Previous : Pointer;
  Next : Pointer;
end;

Procedure TAccessoryList._clone (Var copy : TAccessoryItem; Var original : TAccessoryItem);

 begin

    copy.Id := original.Id;
    copy.Qty := original.Qty;
    copy.Partnumber := original.Partnumber;  **// Access errors happens here**
    copy.Next := Nil;
    copy.Previous := Nil;

  end;

以下のアプリケーションの呼び出し:

  procedure TAccessoryList.AddItem(Var Item : TAccessoryItem);

 Var

    newItem : ptrAccessoryItem;

 begin

    GetMem(newItem, sizeOf(TAccessoryItem));

    _clone(newItem^, Item);

 end;
4

2 に答える 2

21

新しい構造をゼロで初期化する必要があります。GetMemは割り当てられたメモリをゼロにしないため、レコードのフィールドには最初はランダムなガベージが含まれています。あなたは電話する必要があります

FillChar(newItem^, sizeof(TAccessoryItem), 0)

GetMemの後、レコードを使用する前。

理由は次のとおりです。新しく割り当てられたが初期化されていないレコードの文字列フィールドに割り当てると、RTLは、宛先文字列フィールドがnullではない(ガベージポインタを含む)ことを確認し、新しい文字列を割り当てる前に、文字列を逆参照して参照カウントをデクリメントしようとします。フィールドへの文字列値。これは、文字列フィールドまたは変数へのすべての割り当てで必要です。これにより、新しい値が文字列フィールドまたは変数に割り当てられる前に、以前の文字列値が他に使用されていない場合に解放されます。

ポインタがガベージであるため、アクセス違反が発生します...運が良ければ。ランダムガベージポインタが、プロセスで割り当てられたアドレス範囲を指す値である可能性があります。それが起こった場合、割り当ての時点でAVを取得することはありませんが、初期化されていない変数へのこの文字列の割り当てによってプロセスの別の場所でメモリが変更されたため、プログラム実行の後半ではるかに悪化し、はるかに不思議なクラッシュが発生する可能性があります不適切に。

メモリポインタを直接処理していて、割り当てている型にコンパイラ管理フィールドが含まれている場合は常に、コンパイラ管理フィールドが正しく初期化されて破棄されるように、型の初期化と破棄に十分注意する必要があります。

Delphiでレコードを割り当てる最良の方法は、New()関数を使用することです。

New(newItem);

コンパイラは、ポインタの型(ポインタ型が指すサイズ)から割り当てサイズを推測し、メモリを割り当て、すべてのフィールドを適切に初期化します。

対応するデロケーターはDispose()関数です。

Dispose(newItem);

これにより、レコード自体が使用するメモリを解放するだけでなく、レコードのすべてのコンパイラ管理フィールドが正しく破棄されるようになります。

FreeMem(newItem)だけの場合、そのレコード内の文字列およびその他のコンパイラ管理フィールドによって占有されていたメモリが解放されないため、メモリリークが発生します。

コンパイラが管理する型には、長い文字列( "string[10]"ではなく"String")、幅の広い文字列、バリアント、インターフェイス、そしておそらく私が忘れていたものが含まれます。

考える1つの方法はこれです:GetMem/FreeMemは単にメモリのブロックを割り当てて解放します。彼らはそのブロックがどのように使われるかについて何も知りません。ただし、NewとDisposeは、メモリを割り当てたり解放したりするタイプを「認識」しているため、内部のすべてのハウスキーピングが自動的に処理されるように追加の作業を行います。

一般に、本当に必要なのがタイプセマンティクスが関連付けられていない生のメモリブロックだけでない限り、GetMem/FreeMemは避けるのが最善です。

于 2011-02-26T18:32:19.550 に答える
2

また、レコードは(tObjectのような参照型とは対照的に)値型であるため、次のことができます。

AccessoryItem1 := AccessoryItem2;

(または、ポインターの場合はAccessoryItem1 ^:= AccessoryItem2 ^)

そしてそれはあなたのためにすべてのフィールドをコピーします。

同じ理由で、メソッドパラメータとして渡す場合は注意が必要です。ポインタを使用しない限り、delphiは、パラメータとしてメソッドを呼び出すたびに、レコードの新しいコピーを作成します。

于 2011-02-26T20:41:22.457 に答える