2011-06-06 49 views
7

在Delphi中,set如何在内存中组织?集合的内存布局

我尝试做的是铸造一个简单类型的一组类型像

var 
    MyNumber : Word; 
    ShiftState : TShiftState; 
begin 
    MyNumber:=42; 
    ShiftState:=TShiftState(MyNumber); 
end; 

德尔福(2009年)将不会允许这一点,我不明白为什么。如果我得到一个数字,其中单个位编码不同的枚举值并且我可以像这样进行投射,那么这会让我的生活变得更加轻松。这可以做到吗?

一种方法我会去为:

var 
    ShiftState : TShiftState; 
    MyNumber : Word absolute ShiftState; 
begin 
    MyNumber:=42; 
end; 

但这样做我认为我会问的内存布局之前。这比我知道我现在对此有更多的感受。

回答

6

德尔福集是一个位字段,它的位对应于您的集合中元素的关联值。对于一组正常的枚举类型的位布局是直接的:

  • 位0对应于设定元件与顺序值0
  • 位1对应于具有序号值1
  • 等设定元件。

当你处理非连续集合或者集合不是从0开始时,事情会变得有趣。你可以做,使用Delphi的子界类型(例如:set of 3..7)或使用枚举类型,对于元素指定的实际序号值:

type enum=(seven=7, eight=8, eleven=11); 
EnumSet = set of enum; 

在这种情况下Delphi将分配的字节所需要的最小量,其中将包括所有所需的位,但不会“移位”位值以使用更少的空间。在EnumSet示例Delphi将用两个字节:

  • 第一个字节将具有它的第7位与seven
  • 第二个字节相关联的将具有位0与eight
  • 相关联的第二字节将具有第3位与eleven

相关你可以看到一些测试,我做在这里:Delphi 2009 - Bug? Adding supposedly invalid values to a set

测试是使用Delphi 2010完成的,对于Delphi XE没有重复。

+0

Cosmin,非常感谢您对它进行总结!我也非常喜欢阅读关于链接文章中的测试,并推荐大家阅读!现在一切都很清晰,我和编译器都很开心:) – 2011-06-07 07:29:27

1

你必须选择正确大小的序数类型。对我来说(D2007)您的代码编译与MyNumber: Byte

procedure Test; 
var 
    MyNumber: Byte; 
    ShiftState: TShiftState; 
begin 
    MyNumber := 42; 
    ShiftState := TShiftState(MyNumber); 
end; 

我曾经在一些情况下使用这种技术并没有遇到问题。


UPDATE

TShiftState类型已自2010年以来的Delphi扩展到包括两个新的状态,ssTouchssPen和基于所述corresponding doc pagecurrent doc page)。 Delphi 2009 doc仍然将TShiftState定义为一组7个状态。

所以,你尝试转换WordTShiftState会在Delphi 2010+工作,但Byte是德尔福2009年大小合适

+0

感谢您在正确的方向微调!我进一步搜索,并在另一个线程中找到我的答案。顺便说一句:在Delphi XE中'TShiftState'包含9个成员,所以我认为在那里我们必须使'MyNumber'成为'Word',而不是'Byte'了。 – 2011-06-06 13:04:42

+0

感谢编辑,@Andriy! – 2011-06-06 14:23:00

+0

@海因里希:我保持这个页面不会刷新太久,也没有看到您的评论。所以看来你在我做之前找到了解释。不过,我认为我的更新不会伤害Ulrich的答案。 :) – 2011-06-06 14:23:48

1

不幸的是我刚才偶然发现了以下问题:Delphi 2009 - Bug? Adding supposedly invalid values to a set

的Cosmin接受的答案包含了一个非常详细的描述Delphi中的集合。为什么我最好不要使用我的方法absolute。显然,一个设置变量可以占用1到32个字节的内存,具体取决于枚举值。

+0

您应该使用像{$ IF SizeOf(set_in_question)<> SizeOf(typecast_var)} {$ MESSAGE ERROR} – 2011-06-06 18:41:41

0

我使用此:

对于< = 8个元素,PBYTE(@MyNumber)^,用于< = 16个元素,PWORD(@MyNumber)^等

如果枚举更占用空间(通过min枚举大小编译器选项),这仍然会工作。

+0

我会为'Byte(MyEnumVar)'等去添加编译器检查。人们可以写一对函数'OrdToMyEnum'并返回来集中管理。所以如果枚举类型应该增加到“下一个”的大小,你只有一个地方可以改变。 – 2011-06-06 23:36:47