请考虑以下代码。
enum type {CONS, ATOM, FUNC, LAMBDA};
typedef struct{
enum type type;
} object;
typedef struct {
enum type type;
object *car;
object *cdr;
} cons_object;
object *cons (object *first, object *second) {
cons_object *ptr = (cons_object *) malloc (sizeof (cons_object));
ptr->type = CONS;
ptr->car = first;
ptr->cdr = second;
return (object *) ptr;
}
在cons
函数中,变量ptr
的类型为cons_object*
。但在返回值中,它将转换为object*
的类型。
cons_object
和object
是不同的结构。 任何想法!
答案 0 :(得分:32)
这很好,是在C中实现“面向对象”的一种相当常见的技术。因为struct
的内存布局在C中定义良好,只要两个对象共享相同的布局然后你可以安全地在它们之间转换指针。也就是说,type
结构中object
成员的偏移量与cons_object
结构中的相同。
在这种情况下,type
成员会告诉API object
是cons_object
还是foo_object
还是某种其他类型的对象,所以您可能会看到某些内容像这样:
void traverse(object *obj)
{
if (obj->type == CONS) {
cons_object *cons = (cons_object *)obj;
traverse(cons->car);
traverse(cons->cdr);
} else if (obj->type == FOO) {
foo_object *foo = (foo_object *)obj;
traverse_foo(foo);
} else ... etc
}
更常见的是,我似乎将“父”类定义为“子”类的第一个成员,如下所示:
typedef struct {
enum type type;
} object;
typedef struct {
object parent;
object *car;
object *cdr;
} cons_object;
这在很大程度上是相同的,除了你有一个强烈的保证,孩子“类”的记忆布局将与父母相同。也就是说,如果您将一个成员添加到“基础”object
,它将自动被孩子们接收,您不必手动确保所有结构都同步。
答案 1 :(得分:17)
要添加到Dean的答案,这里有一些关于指针转换的内容。我忘记了这个术语是什么,但是指向指针转换的指针不执行任何转换(以与浮点数相同的方式)。它只是对它们指向的位的重新解释(所有这些都是为了编译器的好处)。 “非破坏性转换”我认为是。数据不会改变,只是编译器如何解释指向的内容。
如,
如果ptr
是指向object
的指针,则编译器知道存在具有类型为type
的名为enum type
的特定偏移的字段。另一方面,如果将ptr
强制转换为指向其他类型的指针cons_object
,则它将再次知道如何以类似的方式访问每个cons_object
的字段及其自身的偏移量
为了说明cons_object
的内存布局:
+---+---+---+---+
cons_object *ptr -> | t | y | p | e | enum type
+---+---+---+---+
| c | a | r | | object *
+---+---+---+---+
| c | d | r | | object *
+---+---+---+---+
type
字段偏移0,car
为4,cdr
为8.要访问car字段,编译器需要做的就是添加4
指向结构的指针。
如果指针被强制转换为指向object
的指针:
+---+---+---+---+
((object *)ptr) -> | t | y | p | e | enum type
+---+---+---+---+
| c | a | r | |
+---+---+---+---+
| c | d | r | |
+---+---+---+---+
所有编译器需要知道的是,有一个名为type
的字段,偏移量为0.内存中的内容是什么。
指针甚至不必相关。您可以指向int
并将其转换为指向cons_object
的指针。如果您要访问car
字段,它就像任何普通的内存访问一样。它与结构的开头有一定的偏差。在这种情况下,该内存位置的内容是未知的,但这并不重要。要访问字段,只需要偏移量,并在类型的定义中找到该信息。
指向int
的指针指向内存块:
+---+---+---+---+
int *ptr -> | i | n | t | | int
+---+---+---+---+
转换为cons_object
指针:
+---+---+---+---+
((cons_object *)ptr) -> | i | n | t | | enum type
+---+---+---+---+
| X | X | X | X | object *
+---+---+---+---+
| X | X | X | X | object *
+---+---+---+---+
答案 2 :(得分:9)
使用单独的结构违反了严格的别名规则,并且是未定义的行为:http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
在Dean的最后一个例子中使用嵌入式结构很好。