请考虑以下代码.
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;
}
Run Code Online (Sandbox Code Playgroud)
在cons函数中,变量ptr是类型cons_object*.但是在返回值中它被转换为类型object*.
cons_object它object是不同的结构.有什么想法吗!
Dea*_*ing 32
这很好,是在C中实现"面向对象"的一种相当常见的技术.因为structs 的内存布局在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
}
Run Code Online (Sandbox Code Playgroud)
更常见的是,我似乎将"父"类定义为"子"类的第一个成员,如下所示:
typedef struct {
enum type type;
} object;
typedef struct {
object parent;
object *car;
object *cdr;
} cons_object;
Run Code Online (Sandbox Code Playgroud)
这在很大程度上是相同的,除了你有一个强烈的保证,孩子"类"的记忆布局将与父母相同.也就是说,如果你将一个成员添加到'base' object,它将自动被孩子们接收,你不必手动确保所有结构都是同步的.
Jef*_*ado 18
要添加到Dean的答案,这里有一些关于指针转换的内容.我忘记了这个术语是什么,但是指向指针转换的指针不执行任何转换(以与浮点数相同的方式).它只是对它们指向的位的重新解释(所有这些都是为了编译器的好处)."非破坏性转换"我认为是.数据不会改变,只是编译器如何解释所指向的内容.
例如,
如果ptr是指向a的指针object,则编译器知道存在具有名为typetype 的特定偏移的字段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 *
+---+---+---+---+
Run Code Online (Sandbox Code Playgroud)
该type字段的偏移量为0,car为4,cdr为8.要访问汽车字段,所有编译器需要做的是添加4到结构的指针.
如果指针被强制转换为指向的指针object:
+---+---+---+---+
((object *)ptr) -> | t | y | p | e | enum type
+---+---+---+---+
| c | a | r | |
+---+---+---+---+
| c | d | r | |
+---+---+---+---+
Run Code Online (Sandbox Code Playgroud)
所有编译器需要知道的是,有一个字段被调用type偏移量0.内存中的内容是什么.
指针甚至不必相关.你可以有一个指向an的指针int并将其转换为指向的指针cons_object.如果您要访问该car字段,它就像任何普通的内存访问一样.它与结构的开头有一定的偏差.在这种情况下,该内存位置的内容是未知的,但这并不重要.要访问字段,只需要偏移量,并在类型的定义中找到该信息.
指向int内存块的指针:
+---+---+---+---+
int *ptr -> | i | n | t | | int
+---+---+---+---+
Run Code Online (Sandbox Code Playgroud)
铸成一个cons_object指针:
+---+---+---+---+
((cons_object *)ptr) -> | i | n | t | | enum type
+---+---+---+---+
| X | X | X | X | object *
+---+---+---+---+
| X | X | X | X | object *
+---+---+---+---+
Run Code Online (Sandbox Code Playgroud)
Sec*_*ure 10
使用单独的结构违反了严格的别名规则,并且是未定义的行为:http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
在Dean的最后一个例子中使用嵌入式结构很好.