在C中维护一个指向两个相关类型的指针数组
我的结构如下
struct things { BOOL_T is_copy; /* is false */ handle1 h1; handle2 h2; int n; void * memory; };
有时我会在下面的结构中制作一些物品
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; };
另外,我在一个管理器中有一个结构数组,它将所有的things
和copy_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
成员并决定是将其解释为copy_of_things
还是copy_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
。 在阅读BOOL_T
的内容后,我可以取消引用指向事物或copy_of_things的指针。 我不确定这是否是一个好的解决方案,并提供一个定义良好的行为(在更高的优化级别),因为相同的地址被解释为不同的类型。
问题对于我在任何平台上可移植的问题,是否有更好的解决方案。
编辑
谢谢你的回答。 所以有两个建议的替代方案。
- 使用联盟:方法的缺点是,它需要更多的内存来复制。 就我而言,
sizeof copy_of_things
的sizeof copy_of_things
小于sizeof things
。 一种解决方法是分配足够的字节,其中实际对象可以驻留。 - 使用一个公共结构,并使其成为
copy_of_things
和things
第一个成员。 在这里,我最终会使用2种类型(struct common和struct things或struct copy_of_things)取消引用相同的内存位置。 我不确定严格的别名规则不会咬我。 - 另一个解决方案是将两个结构的第一个成员保持为
char is_copy; /* \0 if not a copy, non zero otherwise
char is_copy; /* \0 if not a copy, non zero otherwise
为char is_copy; /* \0 if not a copy, non zero otherwise
并仅将指针作为char *
things *
或copy_of_things *
或copy_of_things *
。
还是有问题
我见过很多地方使用的解决方案2。 严格的别名规则是否安全? 是否有更好的解决方案来解决我的问题,因为代码将在各种编译器上编译。 反向映射数组的大小很大,所以我避免使用联合或增加反向映射大小的解决方案。 事物(和副本)的数量较少,因此可以在那里添加新的数据成员。
您可以使用更紧凑的联合共享这些结构的一些成员:
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; };
在其他结构中有一个结构是在C中模拟inheritance的常用方法。这里重要的是公共结构应该包含“inheritance”层次结构中所有结构共有的最小数据集,并且它必须始终是inheritance结构中的第一个成员。
关于包含共同成员的第一个重要的事情是,如果没有共同数据,那么结构完全不相关,并且不需要将它们相互关联。
关于将结构放在第一位的另一个重要的事情是,因为公共成员的偏移量将是相同的,并且您可以轻松地将指向更大结构的指针视为指向较小基础结构的指针。
例如,在您的两个第一个结构中, is_copy
成员将具有不同的偏移量,具体取决于您具有指针的结构,因此您需要知道指针指向哪个结构,然后才能访问is_copy
成员,哪种失败你的目的。
将基础结构作为成员放置在扩展结构内的另一种方式就是我上面所说的。
但是,如果您只是使用这两种结构,并且永远不会扩展到更多,那么使用带有联合的版本可能是处理它的最佳方式。
至于可移植性,只要您不在平台之间传输结构或为应用程序的不同部分使用不同的编译器,那么union
版本是大多数源代码可移植的。 “inheritance”方案适用于所有类似现代PC的系统及其编译器,并且已经很长时间了,但不能保证它可以在所有系统和所有编译器上运行(如果您计划移植使用奇怪的硬件和编译器来编写某些罕见系统的代码,但是“inheritance”方案不起作用的情况非常少,并且大多数人在其整个生命周期中都不会接触到这样的系统。 )
我不熟悉这里的许多词语,但是,嘿,请考虑我的以下建议:
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; }; };
看起来非常类似于您问题中的那个,尽管只有一个关键区别。 注意变量名后面的星号,使它们成为结构的指针。
t
和c
都具有相同的大小,因此在使用union的任何一个分支时都不会有无关的内存。 它有点类似于sclarke81的答案 ,但是在这里它更加清晰,你也不必将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; }
我不知道这是否涵盖了你的需求,只是一个提示。
这样的事情怎么样:
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
有一个指向你对象的指针,而enum告诉你如何解释它。 您可以根据需要malloc并释放对象的内存。
另一种方法是取消枚举:
struct thing_array_item { struct things *obj1; struct copy_of_things *obj2; };
有了这个,你只需将你没有使用的指针设置为NULL
。 然后,任何thing_array_item
的非空指针都是您使用的指针。
我对我的问题有很多好主意。 但似乎我无法非常有效地记录我的问题的所有细节。
这是我提出的最终解决方案:
[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 *
这为我的代码添加了非常轻微的不可读性,但是满足了我的所有其他要求。