2011-06-06 47 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