2014-07-25 15 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移动到非常大的反向映射数组中。保持每个对象对我来说都是有益的。 –