在x64中枚举集合的大小

7

我发现在32位和64位系统中,SizeOf(set)的值不同。下面的示例显示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.

1
这对你有什么影响呢?似乎64位编译器在使用64位整数进行设置,但32位编译器则完全做了另一件事情。谁知道呢。看看编译器如何处理较小基本类型的集合会很有趣。我确定没有文档和编译器开关。你必须接受这个现实。 - David Heffernan
1
我以以下方式将一组保存到流中:Stream.Write(set, SizeOf(set))。因此,32位和64位应用程序创建不同的流。我检查了较小的集合(最多32个元素),它们的大小相等。嘿,我检查了一个有66个元素的集合,32位和64位的sizeof都是9。 - Denis Sletkov
1
非常相关的问题(几乎是重复的):如何保存/加载一组类型。这些答案展示了将一组转换为一堆位的各种方法。 - GolezTrol
保存为二进制文件时,应该只保存足够的字节,不要多余,我想当只需要3个字节时,保存4个字节是没有意义的。因此,我会根据基本枚举的大小来确定持久性。并希望您永远不要更改基本枚举! - David Heffernan
2
值得一提的是,文档(http://docwiki.embarcadero.com/RADStudio/XE8/en/Internal_Data_Formats#Set_Types)中指出:“如果可能的话,编译器会将集合存储在CPU寄存器中”。在64位系统中,寄存器可以达到64位。 - Rudy Velthuis
2个回答

6
回答您的问题。我在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个字节。
要解决这个问题,您需要使用像WriteSetTWriter.WritePropertyTReader.ReadSet中或者您可以做以下事情:
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;

2
所以这似乎是合理的。编译器在可能的情况下使用硬件支持的类型,然后当它们用尽时,使用非本机类型。 - David Heffernan
对我来说似乎很合理:由于Set按位运作,因此需要8个元素的8位,即1字节。再增加一个元素将需要1个字节,直到16位/元素被填满。以此类推。当然,为了在速度和内存之间做出妥协,在平台内不使用奇数大小(3、5、6或7字节)。 - AmigoJack

0

另一个解决方法是创建一个函数,以确保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)));

显然只能用于集合。


创建解决方法没有问题。但我不明白这是编译器的错误还是编译器的特性。 - Denis Sletkov
@dsm 你需要小心使用这个选项从流中读取数据,因为你会覆盖在 set 后面的变量。 - Graymatter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接