26

C と同じレイアウトを含むレコードを Delphi で宣言したいと考えています。

興味のある方へ : このレコードは、Windows OS の LDT_ENTRY レコードのユニオンの一部です。(私は Delphi で Xbox エミュレーターに取り組んでいるため、Delphi でこのレコードを使用する必要があります。sourceforge のプロジェクト Dxbx を参照してください)。

とにかく、問題のレコードは次のように定義されています。

    struct
    {
        DWORD   BaseMid : 8;
        DWORD   Type : 5;
        DWORD   Dpl : 2;
        DWORD   Pres : 1;
        DWORD   LimitHi : 4;
        DWORD   Sys : 1;
        DWORD   Reserved_0 : 1;
        DWORD   Default_Big : 1;
        DWORD   Granularity : 1;
        DWORD   BaseHi : 8;
    }
    Bits;

私の知る限り、Delphi では可能なビット フィールドはありません。私はこれを試しました:

 Bits = record
      BaseMid: Byte; // 8 bits
      _Type: 0..31; // 5 bits
      Dpl: 0..3; // 2 bits
      Pres: Boolean; // 1 bit
      LimitHi: 0..15; // 4 bits
      Sys: Boolean; // 1 bit
      Reserved_0: Boolean; // 1 bit
      Default_Big: Boolean; // 1 bit
      Granularity: Boolean; // 1 bit
      BaseHi: Byte; // 8 bits
  end;

しかし悲しいことに、サイズは予想される 4 ではなく 10 バイトになります。同じレイアウト、同じサイズ、同じメンバーを持つレコードを取得するために、レコードを宣言する方法を知りたいです。ゲッター/セッターの負荷がないことが望ましいです。

ティア。

4

4 に答える 4

32

みんな、ありがとう!

この情報に基づいて、これを次のように減らしました。

RBits = record
public
  BaseMid: BYTE;
private
  Flags: WORD;
  function GetBits(const aIndex: Integer): Integer;
  procedure SetBits(const aIndex: Integer; const aValue: Integer);
public
  BaseHi: BYTE;
  property _Type: Integer index $0005 read GetBits write SetBits; // 5 bits at offset 0
  property Dpl: Integer index $0502 read GetBits write SetBits; // 2 bits at offset 5
  property Pres: Integer index $0701 read GetBits write SetBits; // 1 bit at offset 7
  property LimitHi: Integer index $0804 read GetBits write SetBits; // 4 bits at offset 8
  property Sys: Integer index $0C01 read GetBits write SetBits; // 1 bit at offset 12
  property Reserved_0: Integer index $0D01 read GetBits write SetBits; // 1 bit at offset 13
  property Default_Big: Integer index $0E01 read GetBits write SetBits; // 1 bit at offset 14
  property Granularity: Integer index $0F01 read GetBits write SetBits; // 1 bit at offset 15
end;

インデックスは次のようにエンコードされます(BitOffset shl 8) + NrBits。1<=NrBits<=32 および 0<=BitOffset<=31

これで、これらのビットを次のように取得および設定できます。

{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
function RBits.GetBits(const aIndex: Integer): Integer;
var
  Offset: Integer;
  NrBits: Integer;
  Mask: Integer;
begin
  NrBits := aIndex and $FF;
  Offset := aIndex shr 8;

  Mask := ((1 shl NrBits) - 1);

  Result := (Flags shr Offset) and Mask;
end;

procedure RBits.SetBits(const aIndex: Integer; const aValue: Integer);
var
  Offset: Integer;
  NrBits: Integer;
  Mask: Integer;
begin
  NrBits := aIndex and $FF;
  Offset := aIndex shr 8;

  Mask := ((1 shl NrBits) - 1);
  Assert(aValue <= Mask);

  Flags := (Flags and (not (Mask shl Offset))) or (aValue shl Offset);
end;

かなり気の利いたと思いませんか?!?!

PS: Rudy Velthuis は、彼の優れた「転換の落とし穴」の記事に、これの改訂版を含めました。

于 2008-11-11T22:39:52.880 に答える
16

Rudy の Delphi コーナーは、Delphi と C/C++ の相互運用性に関して私が知っている最高のリソースです。彼の変換の落とし穴は、Delphi で C/C++ API を使用する際に必ず読む必要があります。あなたが最も興味を持っている章はレコードとアライメント -> ビットフィールドですが、全体を上から下まで2 回読むことを強くお勧めします。他の記事も間違いなく時間を投資する価値があります。

于 2008-11-11T21:00:32.750 に答える
5

わかりました、私のビット操作は少しさびているので、バイトを逆にすることができました. しかし、以下のコードは一般的な考え方を示しています。

type
  TBits = record
  private
    FBaseMid     : Byte;
    FTypeDplPres :  Byte;
    FLimitHiSysEa: Byte;
    FBaseHi      : Byte;

    function GetType: Byte;
    procedure SetType(const AType: Byte);
    function GetDpl: Byte;
    procedure SetDbl(const ADpl: Byte);
    function GetBit1(const AIndex: Integer): Boolean;
    procedure SetBit1(const AIndex: Integer; const AValue: Boolean);
    function GetLimitHi: Byte;
    procedure SetLimitHi(const AValue: Byte);
    function GetBit2(const AIndex: Integer): Boolean;
    procedure SetBit2(const AIndex: Integer; const AValue: Boolean);

  public
    property BaseMid: Byte read FBaseMid write FBaseMid;
    property &Type: Byte read GetType write SetType; // 0..31
    property Dpl: Byte read GetDpl write SetDbl; // 0..3
    property Pres: Boolean index 128 read GetBit1 write SetBit1; 
    property LimitHi: Byte read GetLimitHi write SetLimitHi; // 0..15

    property Sys: Boolean index 16 read GetBit2 write SetBit2; 
    property Reserved0: Boolean index 32 read GetBit2 write SetBit2; 
    property DefaultBig: Boolean index 64 read GetBit2 write SetBit2; 
    property Granularity: Boolean index 128 read GetBit2 write SetBit2; 
    property BaseHi: Byte read FBaseHi write FBaseHi;
  end;

  function TBits.GetType: Byte;
  begin
    Result := (FTypeDplPres shr 3) and $1F;
  end;

  procedure TBits.SetType(const AType: Byte);
  begin
    FTypeDplPres := (FTypeDplPres and $07) + ((AType and $1F) shr 3);
  end;

  function TBits.GetDpl: Byte;
  begin
    Result := (FTypeDplPres and $06) shr 1;
  end;

  procedure TBits.SetDbl(const ADpl: Byte);
  begin
    FTypeDblPres := (FTypeDblPres and $F9) + ((ADpl and $3) shl 1);
  end;

  function TBits.GetBit1(const AIndex: Integer): Boolean;
  begin
    Result := FTypeDplPres and AIndex = AIndex;
  end;

  procedure TBits.SetBit1(const AIndex: Integer; const AValue: Boolean);
  begin
    if AValue then
      FTypeDblPres := FTypeDblPres or AIndex
    else
      FTypeDblPres := FTypeDblPres and not AIndex;
  end;

  function TBits.GetLimitHi: Byte;
  begin
    Result := (FLimitHiSysEa shr 4) and $0F;
  end;

  procedure TBits.SetLimitHi(const AValue: Byte);
  begin
    FLimitHiSysEa := (FLimitHiSysEa and $0F) + ((AValue and $0F) shr 4);
  end;

  function TBits.GetBit2(const AIndex: Integer): Boolean;
  begin
    Result := FLimitHiSysEa and AIndex = AIndex;
  end;

  procedure TBits.SetBit2(const AIndex: Integer; const AValue: Boolean);
  begin
    if AValue then
      FLimitHiSysEa := FLimitHiSysEa or AIndex
    else
      FLimitHiSysEa := FLimitHiSysEa and not AIndex;
  end;
于 2008-11-11T21:56:53.730 に答える
0

基本的には、ビット操作で汚れを落とす必要があります。

具体的には、なぜその構造を維持する必要があるのでしょうか?

この方言 (TCP/IP など) で通信するか、この方法でデータを格納する (ファイルなど) レガシー プログラムとのみ通信する必要がある場合は、通常の Delphi 構造をビット バージョンにマップします。互換性。つまり、通常の構造の Delphi 構造をメモリ内で使用し、互換性のある方法でその構造を読み書きするコードを記述します。

メモリを節約する必要がある場合は、内部整数のビットなどを操作するゲッターとセッターを作成します。これはパフォーマンスに影響を与えますが、元の C プログラムに比べてそれほど大きな影響はありません。唯一の違いは、ビット操作が C バージョンではコンパイラ マジックによって追加されるのに対し、自分で作成する必要があることです。

メモリ内に多くのレコードがなく、別のプログラムと対話する必要がない場合は、自然な Delphi 構造を使用します。より高いパフォーマンスとのトレードオフは、より多くのメモリを使用することになります。

しかし、それはすべてあなたの基準に依存します。

いずれにせよ、Delphi コンパイラに C コンパイラと同じ仕事をさせることはできません。

ここで別の人によって提案された PACKED RECORD は、それを行わず、意図されたものでもありませんでした。32 ビット境界などに整数を配置するためのアラインメント パディングのみが削除されますが、複数のフィールドが 1 バイトにパックされることはありません。

これを行う一般的な方法は、ビット フィールドを使用して内部的に実装する Delphi SETS を使用することに注意してください。繰り返しますが、C バリアントとは異なるコードになります。

于 2008-11-11T20:30:27.990 に答える