题
我想在Delphi中声明一个包含与C中相同布局的记录。
对于那些感兴趣的人:此记录是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。
解决方案
谢谢大家!
根据这些信息,我将其减少为:
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 <!> lt; = NrBits <!> lt; = 32且0 <!> lt; = BitOffset <!> lt; = 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现在在其出色的中加入了修订版本<!>转换<!>“-article 的陷阱。
其他提示
Rudy的Delphi Corner 是我所知道的有关Delphi和C / C ++互操作性的最佳资源。在Delphi中使用C / C ++ API时,他的转换陷阱几乎是必读的。您最感兴趣的章节是记录和对齐 - <!> gt; Bitfields ,但我建议你从上到下阅读整个内容,两次。其他文章也绝对值得花时间投资。
好的,我的位操作有点生疏,所以我可以颠倒字节。但是下面的代码给出了一般的想法:
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;
嗯,你基本上需要通过位操作来解决这个问题。
具体而言,为什么需要保留该结构?
如果您只需要与使用此方言(TCP / IP或类似方式)进行通信的旧程序进行通信,或以这种方式存储数据(文件等),那么我会将普通的Delphi结构映射到位版兼容。换句话说,我会在内存中使用通常结构化的Delphi结构,并编写代码以兼容的方式写入和读取该结构。
如果你需要节省内存,我会制作操作内部整数或类似内容的getter和setter。这会产生性能影响,但不会超过原始C程序的影响,唯一的区别是C语言中的编译魔术会添加位操作,而您必须自己编写它。 / p>
如果内存中没有很多记录,并且不需要与另一个程序通信,我会使用自然的Delphi结构。为获得更高性能而进行权衡将会消耗更多内存。
但这一切都取决于你的标准。
在任何情况下,您都无法与Delphi编译器讨论为C编译器执行相同的工作。
另一个人在这里提出的包装记录不会这样做,而且从来没有意图。它只会删除对齐填充以将整数放在32位边界上并且类似,但不会将多个字段打包成一个字节。
请注意,执行此操作的常用方法是通过Delphi SETS,它使用位字段在内部实现。同样,您将拥有与C变体不同的代码。