2014-07-25 19 views
0

我具有這樣的結構,如下保持指針的C中的陣列指向兩個相關類型

struct things 
{ 
    BOOL_T is_copy; /* is false */ 
    handle1 h1; 
    handle2 h2; 
    int  n; 
    void * memory; 
}; 

有時我使things對象的拷貝在下面結構

struct copy_of_things 
{ 
    BOOL_T is_copy; /* is true */ 
    handle1 h1; /* I don't need h2 and n here */ 
    void * memory; /* copied from things object and 
         things object allocates new memory */ 
    int  another_member; 
}; 

此外,我在管理器中有一個結構陣列,可以讓所有的thingscopy_of_things結構保持在我的程序中(稱爲struct things *things_array[SIZE_OF_ARRAY];)。由於設計要求,我無法管理2個數組(陣列與散列相似)。爲了實現這一目標,我做了這個數組的類型,thing *和改變的copy_of_things類型如下

struct copy_of_things 
{ 
    struct things ths; 
    int another_member; 
}; 

現在我可以閱讀我的數組元素的is_copy成員,並決定是否將其解釋爲thingscopy_of_things

我覺得這不僅是inefficient在內存方面,但ugly看。

解決方案2 我也打算使用的數組類型是struct of type(is_copy) and a union

struct things { 
    BOOL_T is_copy; 
    union { 
    struct { /* is_copy = false */ 
     handle1 h1; 
     handle2 h2; 
     int  n; 
     void * memory; 
    } t; 
    struct { /* is_copy = true */ 
     handle1 h1; 
     void * memory; 
     int  another_member; 
    } c; 
}; 

但是,雖然審查我發現這種設計也很醜。

解決方案3我打算保留BOOL_T is_copy;作爲兩種結構的第一個成員,並保留BOOL_T類型的數組。在閱讀BOOL_T的內容後,我可以取消引用我的指針或copy_of_things。我不確定這是否是一個好的解決方案,並提供了一個定義良好的行爲(在更高級別的優化中),因爲相同的地址被解釋爲不同的類型。

問題 是否有更好的解決方案可以解決我的問題,在任何平臺上都是可移植的。

編輯
謝謝你的答案。所以有兩個建議的選擇。

  1. 使用聯盟:該方法的缺點是,它需要更多的副本內存。在我的情況下,sizeof copy_of_things明顯小於sizeof things。一種解決方法是分配恰好足夠的字節,其中實際的對象可以駐留。
  2. 使用一個公用的結構體並使其成爲copy_of_thingsthings的第一個成員。在這裏,我最終會用兩種類型(struct common和struct things或struct copy_of_things)去引用相同的內存位置。我不知道strict aliasing rule不會咬我。
  3. 還有一種解決方案可以將兩個結構的第一個成員保存爲char is_copy; /* \0 if not a copy, non zero otherwise,並僅訪問指針char *things *copy_of_things *

仍然懸而未決的問題
我見過的解決方案2中使用的許多地方。是否嚴格別名規則安全?由於代碼將在各種編譯器上編譯,是否有更好的解決方案?反向映射數組的大小很大,所以我避免使用聯合或增加反向映射大小的解決方案。事物(和副本)的數量較少,所以可以在那裏添加新的數據成員。

+0

你能不能讓兩個結構都是同一個類型?而在「copy」語義中,'h2'沒有被使用,'n'被重用? –

+0

@MattMcNabb謝謝。實際結構包含大約12個成員,並且類型不能全部重用。但是,是的,我明白了你的想法,並會檢查這是否解決了我的問題。 –

回答

0

我對我的問題有很多好主意。但似乎我無法非常有效地記錄我的問題的所有細節。

這裏是我想出了最終的解決方案:

[1]編寫包含由通過反向映射訪問成員的共同結構。

struct common { 
    handle1 h1; 
    void * memory; 
}; 

[2]現在擁有這個構件在兩個結構。

struct things 
{ 
    handle2 h2; 
    int  n; 
    struct common cmn; 
}; 
struct copy_of_things 
{ 
    BOOL_T is_copy; /* is true */ 
    int  another_member; 
    struct common cmn; 
}; 

[3]反向映射類型更改爲struct common *

這增加了很輕微的非可讀性我的代碼,但符合我的所有其他要求。

3

您可以用更緊湊的工會分享這些結構的一些成員:

struct things { 
    BOOL_T is_copy; 
    handle1 h1; 
    void * memory; 
    union { 
    struct { /* is_copy = false */ 
     handle2 h2; 
     int  n; 
    } t; 
    struct { /* is_copy = true */ 
     int  another_member; 
    } c; 
}; 
+0

謝謝你的回答。如果'things'中有許多成員與'copy_of_things'相比,這將會是空間效率低下的。 –

1

具有其它結構的內部結構是最重要的事情在這裏C.模仿繼承的一種常見方式是常見的結構應該包含「繼承」層次結構中所有結構共有的最小數據集,並且它必須始終是繼承結構中的第一個成員。

關於包含普通成員的第一個重要的事情是,如果沒有共同的數據,那麼這些結構是完全無關的,並且不需要將它們相互關聯。

關於將結構放在第一位,另一個重要的事情是,因爲常見成員的偏移量將是相同的,並且您可以輕鬆地將指向較大結構的指針視爲指向較小基礎結構的指針。


例如,在你的兩個第一結構中,is_copy成員將有不同取決於你有一個指針,該結構失調,所以你需要知道的指針指向,然後才能訪問其結構is_copy會員,哪種會打敗你的目的。

將基礎結構作爲構件放置在擴展結構內的另一種方式就是我上面所談論的。


但是,如果你僅僅使用這兩個結構,也絕不會擴展至更多,然後使用版本與工會可能可能是把它處理的最佳方式。


至於便攜性,只要你不轉移平臺或使用不同的編譯器爲您的應用程序的不同部分之間的結構,那麼union版本是最源代碼的可移植性。 「繼承」方案將適用於所有現代類PC系統及其編譯器,並且已經這麼做了很長時間,但不能保證它可以在所有系統和所有編譯器上工作(如果您計劃移植代碼到一些罕見的系統中,可能需要注意一些奇怪的硬件和編譯器,但是「繼承」方案不起作用的情況很少,大多數人都不會接觸到這樣的系統壽命)

+0

謝謝你的回答。所以工會看起來是最好的決定。雖然副本很少使用,但我理解並同意你的推理。 –

0

怎麼是這樣的:。

enum object_types 
{ 
    thing, 
    copy 
}; 

struct thing_array_item 
{ 
    void *object; 
    enum object_types obj_type; 
}; 

struct thing_array_item *things_array[ SIZE_OF_ARRAY ]; 

這樣做意味着一個thing_array_item有一個指針指向你的對象和枚舉電話你是如何解釋它的。你可以只需malloc,然後根據需要釋放對象的內存。

另一種方法是廢除枚舉:

struct thing_array_item 
{ 
    struct things *obj1; 
    struct copy_of_things *obj2; 
}; 

有了這個,你只希望設置你不使用到NULL指針。然後,任何thing_array_item中的非空指針都是您使用的指針。

+0

解決方案1很有趣。解決方案2需要反覆映射的內存大小的兩倍。 –

1

我不熟悉這裏的許多話,但嘿,考慮我的以下建議:

struct things 
{ 
    BOOL_T is_copy; 
    union 
    { 
     struct 
     { 
      handle1 h1; 
      handle2 h2; 
      int  n; 
      void * memory; 
     } * t; 
     struct 
     { 
      handle1 h1; 
      void * memory; 
      int another_member; 
     } * c; 
    }; 
}; 

看起來非常相似,一個在你的問題,但有一個關鍵的區別。注意變量名稱後面的星號,使它們成爲結構的指針。

這兩個tc將具有相同的大小,因此在使用聯合的任何一個分支時不會有無關的內存。這有點類似於sclarke81's answer,但是在這裏它更加明顯,您也不需要將void * object轉換爲您分配內存訪問其成員的結構。即,可以只寫:

if (mything.is_copy) 
{ 
    mything.c->another_member; 
} else { 
    mything.t->n; 
} 

相反的:

if (mything.obj_type == copy) 
{ 
    ((struct copy_of_things) mything.object)->another_member; 
} else { 
    ((struct things) mything.object)->n; 
} 

我不知道,如果覆蓋你的需求,只是一個提示。

+0

謝謝你的幫助。這是一個有趣的建議。但是這也會將is_copy移動到非常大的反向映射數組中。保持每個對象對我來說都是有益的。 –