2008-11-11 27 views
24

我想聲明的德爾福記錄包含相同的佈局,因爲它在C.如何模擬Delphi記錄中的位域?

對於那些有興趣:這記錄是在Windows操作系統的LDT_ENTRY記錄工會的一部分。 (我需要在Delphi中使用此記錄,因爲我正在使用Delphi中的Xbox模擬器 - 請參閱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; 

但很可惜:它的大小變爲10個字節,而不是預期的4 我想知道我應該如何申報備案,讓我得到一個紀錄,相同的佈局,相同的大小和相同的成員。最好不加載吸氣/吸氣裝置。

TIA。

回答

28

謝謝大家!

基於該信息,我減少這對:

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現在在他出色的"Pitfalls of converting"-article中包含了這個修訂版本。

+0

這真是個好主意! – gabr 2008-11-12 07:40:34

0

那麼,你基本上需要用位操作來弄髒髒。

爲什麼您需要保留該結構?

如果您只需要與以這種方式進行對話的傳統程序(TCP/IP或類似方式)進行通信,或以這種方式存儲數據(文件等),那麼我會將普通的Delphi結構映射到一個版本兼容。換句話說,我會在內存中使用通常結構化的Delphi結構,並編寫代碼以兼容的方式編寫和讀取該結構。

如果你需要節省內存,我會讓getter和setter來操作內部整數或類似的位。這會對性能產生影響,但不會比原來的C程序多得多,唯一的區別是在C版本中編譯器魔術會添加位操作,而您必須自己編寫它。

如果你在內存中沒有很多記錄,並且不需要與其他程序交談,我會使用自然的Delphi結構。權衡更高的性能將會使用更多的內存。

但這一切都取決於您的標準。

在任何情況下,您都無法將Delphi編譯器與C編譯器做同樣的工作。

PACKED RECORD,在另一個人的建議下,並沒有這樣做,也從來沒有這樣做過。它只會刪除對齊填充以將整數放在32位邊界上並類似,但不會將多個字段打包到一個字節中。

請注意,一個常見的方法是通過Delphi SETS,它們在內部使用位域實現。再次,您將擁有與C變體不同的代碼。

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;