C语言 将一个结构指针转换为另一个 - C

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3766229/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:29:26  来源:igfitidea点击:

Casting one struct pointer to another - C

cstructcasting

提问by Navaneeth K N

Please consider the following code.

请考虑以下代码。

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;
}

In the consfunction, variable ptris of type cons_object*. But in the return value it is converted to type of object*.

cons函数中,变量ptr的类型是cons_object*。但是在返回值中它被转换为object*.

  1. I am wondering how this is possible because cons_objectand objectare different structs.
  2. Are there any issues in doing stuff like this?
  1. 我想知道这是怎么可能的,因为cons_objectobject是不同的结构。
  2. 做这样的事情有什么问题吗?

Any thoughts!

有什么想法吗!

回答by Dean Harding

This is fine and is a fairly common technique for implementing "object-orientation" in C. Because the memory layout of structs is well-defined in C, as long as the two object share the same layout then you can safely cast pointers between them. That is, the offset of the typemember is the same in the objectstruct as it is in the cons_objectstruct.

这很好,并且是在 C 中实现“面向对象”的一种相当常见的技术。因为structs的内存布局在 C 中是明确定义的,只要两个对象共享相同的布局,那么您就可以安全地在它们之间投射指针. 也就是说,type成员的偏移量在object结构体中与在结构体中的偏移量相同cons_object

In this case, the typemember tells the API whether the objectis a cons_objector foo_objector some other kind of object, so you might be see something like this:

在这种情况下,type成员告诉 APIobject是 acons_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
}

More commonly, I've seem implementations where the "parent" class is defined as the first member of the "child" class, like so:

更常见的是,我似乎实现了将“父”类定义为“子”类的第一个成员的实现,如下所示:

typedef struct {
    enum type type;
} object;

typedef struct {
    object parent;

    object *car;
    object *cdr;
} cons_object;

This works in largely the same way, except you've got a strong gaurantee that the memory layout of the child "classes" will be the same as the parents. That is, if you add a member to the 'base' object, it'll automatically be picked up by the children and you won't have to manually make sure all of the structures are in sync.

这在很大程度上以相同的方式工作,除了您有一个强有力的保证,即子“类”的内存布局将与父母相同。也就是说,如果您将成员添加到 'base' object,它会自动被孩子们挑选,您不必手动确保所有结构都同步。

回答by Jeff Mercado

To add to Dean's answer, here's something about pointer conversions in general. I forgot what the term is for this, but a pointer to pointer cast performs no conversion (in the same way int to float is). It is simply a reinterpretation of the bits that they point to (all for the compiler's benefit). "Non-destructive conversion" I think it was. The data doesn't change, only how the compiler interprets what is being pointed at.

为了补充 Dean 的回答,这里有一些关于指针转换的一般内容。我忘记了这个术语是什么,但是指向指针强制转换的指针不执行转换(与 int 到 float 的方式相同)。它只是对它们指向的位的重新解释(所有这些都是为了编译器的利益)。“非破坏性转换”我认为是。数据不会改变,只会改变编译器如何解释所指向的内容。

e.g.,
If ptris a pointer to an object, the compiler knows that there is a field with a particular offset named typeof type enum type. On the other hand if ptris cast to a pointer to a different type, cons_object, again it will know how to access fields of the cons_objecteach with their own offsets in a similar fashion.

例如,
Ifptr是指向 an 的指针object,编译器知道有一个具有特定偏移量的字段,名为typetype enum type。另一方面,如果ptr被强制转换为指向不同类型的指针,cons_object,它将再次知道如何以cons_object类似的方式访问每个具有自己偏移量的字段。

To illustrate imagine the memory layout for a cons_object:

为了说明,想象一下 a 的内存布局cons_object

                    +---+---+---+---+
cons_object *ptr -> | t | y | p | e | enum type
                    +---+---+---+---+
                    | c | a | r |   | object *
                    +---+---+---+---+
                    | c | d | r |   | object *
                    +---+---+---+---+

The typefield has offset 0, caris 4, cdris 8. To access the car field, all the compiler needs to do is add 4to the pointer to the structure.

type字段的偏移量为 0,car为 4,cdr为 8。要访问 car 字段,编译器所需要做的就是添加4指向结构的指针。

If the pointer was cast to a pointer to an object:

如果指针被强制转换为指向 an 的指针object

                    +---+---+---+---+
((object *)ptr)  -> | t | y | p | e | enum type
                    +---+---+---+---+
                    | c | a | r |   |
                    +---+---+---+---+
                    | c | d | r |   |
                    +---+---+---+---+

All the compiler needs to know is that there is a field called typewith offset 0. Whatever is in memory is in memory.

编译器只需要知道有一个type偏移量为 0的字段。内存中的任何内容都在内存中。

The pointers don't even have to be related whatsoever. You can have a pointer to an intand cast it to a pointer to an cons_object. If you were to access the carfield, it's just like any ordinary memory access. It has a certain offset from the beginning of the structure. In this case, what is in that memory location is unknown but that's unimportant. To access a field, only the offset is needed and that information is found in the type's definition.

指针甚至不必有任何关联。您可以拥有一个指向 an 的指针int并将其强制转换为指向 an 的指针cons_object。如果您要访问该car字段,就像任何普通的内存访问一样。它与结构的开头有一定的偏移。在这种情况下,该内存位置中的内容是未知的,但这并不重要。要访问一个字段,只需要偏移量,并且该信息可以在类型的定义中找到。

A pointer to an intpoints to a block of memory:

指向 an 的指针指向int一块内存:

                        +---+---+---+---+
int             *ptr -> | i | n | t |   | int
                        +---+---+---+---+

Casted to a cons_objectpointer:

转换为cons_object指针:

                        +---+---+---+---+
((cons_object *)ptr) -> | i | n | t |   | enum type
                        +---+---+---+---+
                        | X | X | X | X | object *
                        +---+---+---+---+
                        | X | X | X | X | object *
                        +---+---+---+---+

回答by Secure

Using separate structs violates the strict aliasing rule and is undefined behaviour: http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

使用单独的结构违反了严格的别名规则并且是未定义的行为:http: //cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

Using an embedded struct as in Dean's last example is fine.

在 Dean 的最后一个示例中使用嵌入式结构很好。