【问题标题】:Enumeration set size in x64x64 中的枚举集大小
【发布时间】:2015-07-31 22:19:30
【问题描述】:

我发现 SizeOf(set) i 在 32 位和 64 位中不同,下面的示例显示 32 位为 5 个字节,64 位为 8 个字节。但我没有发现任何关于 64 位 SizeOf(sets) 变化的信息。是否有任何关于它的 Embarcadero 文档或编译器指令可以在 32 位和 64 位上获得类似的结果。

program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses System.SysUtils;

type
{ Enumeration of properties}
TProperty1 = (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14,
  p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28,
  p29, p30, p31, p32, p33, p34, p35, p36, p37);

TProperties1 = set of TProperty1;

begin
WriteLn(SizeOf(TProperties1));
ReadLn;
end.

【问题讨论】:

  • 为什么这对你很重要?似乎 64 位编译器使用 64 位整数作为集合,但 32 位编译器完全做了其他事情。天知道是什么。看看编译器对较小的基本类型集做了什么会很有趣。我确定没有文档,也没有编译器开关。你必须忍受这个。
  • 我通过以下方式将集合保存到流中 Stream.Write(set, SizeOf(set))。所以 32 位和 64 位应用程序创建不同的流。我检查了一个较小的集合(最多 32 个元素),它们的大小是相等的。嘿,我检查了一个包含 66 个元素的集合,并且 sizeof 在 32 位和 64 位上为 9。
  • 非常相关的问题(几乎是重复的):How to save/load set of types。显示将一组转换为一堆位的各种方法的答案。
  • 要保存为二进制文件,你应该只保存足够的字节数,我想当 3 个字节足够时保存 4 个字节是没有意义的。因此,我将持久性基于基本枚举的大小。并希望您永远不要更改基本枚举!!!
  • FWIW;文档 (docwiki.embarcadero.com/RADStudio/XE8/en/…) 说:“如果可能,编译器会将集合存储在 CPU 寄存器中”。在 64 位中,寄存器可以达到 64 位。

标签: delphi enums 64-bit 32bit-64bit 32-bit


【解决方案1】:

回答你的问题。我在 Embarcadero 网站上找不到任何关于差异或改变行为的编译器指令的信息。我的研究表明:

集合在 32 位中具有以下字节大小:

  • 最多 8 个元素 - 1 个字节
  • 9 到 16 个元素 - 2 字节
  • 17 到 32 个元素 - 4 字节

从这一点开始,它会根据需要添加字节,一次一个。所以 33 到 40 个元素使用 5 个字节,41 到 48 个元素使用 6 个字节。

在 64 位模式下,情况略有不同:

  • 最多 8 个元素 - 1 个字节
  • 9 到 16 个元素 - 2 字节
  • 17 到 32 个元素 - 4 字节
  • 33 到 64 个元素 - 8 字节

从这一点开始,它会根据需要添加字节,一次一个。所以 65 到 72 个元素使用 9 个字节,73 到 80 个元素使用 10 个字节。

要解决这个问题,您需要在 TWriter.WritePropertyTReader.ReadSet 中使用类似 WriteSet 的内容,或者您​​可以执行以下操作:

procedure SaveSetToStream(aStream: TStream; const aSet: TProperties1);
var
  streamData: array[0..7] of byte;
begin
  Assert(SizeOf(aSet) <= SizeOf(streamData), 'Set is too large to save. Increase the array length.');
  FillChar(streamData, SizeOf(streamData), 0);
  Move(aSet, streamData, SizeOf(aSet));
  aStream.Write(streamData, SizeOf(streamData));
end;

function ReadFromStream(aStream: TStream): TProperties1;
var
  streamData: array[0..7] of byte;
begin
  Assert(SizeOf(Result) <= SizeOf(streamData), 'Set is too large to load. Increase the array length.');
  aStream.Read(streamData, SizeOf(streamData));
  Move(streamData, Result, SizeOf(Result));
end;

【讨论】:

  • 所以这似乎是合理的。编译器尽可能使用硬件支持的类型,然后在用完它们时使用非本机类型。
  • 对我来说似乎是合乎逻辑的:因为Set 按位工作,8 个元素需要 8 位,因此需要 1 个字节。多一个元素将需要多 1 个字节,直到 16 位/元素已满。等等。当然,作为速度/内存的折衷方案,平台内不会使用奇数大小(3、5、6 或 7 字节)。
【解决方案2】:

另一种解决方法,确保 32 位机器可以从 64 位机器读取流,反之亦然是创建一个函数

function SizeCheck( const p : integer ) : integer;
begin
  if p in [5..8 ] then Result := 8 else Result := p; // adjust for 64 bit set sizes
end;  

然后使用

Stream.Write(set, SizeCheck(SizeOf(set)));

显然只用于集合。

【讨论】:

  • 创建变通方法没有问题。但我不明白这是编译器错误还是编译器功能。
  • @dsm 使用此选项从流中读取时需要小心,因为您将覆盖集合后面的变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-23
  • 2012-03-15
  • 2017-03-02
  • 1970-01-01
相关资源
最近更新 更多