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

Nav*_*K N 27 c struct casting

请考虑以下代码.

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*.

  1. 我想知道这是怎么可能的,因为cons_objectobject是不同的结构.
  2. 做这样的事情有什么问题吗?

有什么想法吗!

Dea*_*ing 32

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

在这种情况下,type成员告诉API是否object是一种cons_objectfoo_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,它将自动被孩子们接收,你不必手动确保所有结构都是同步的.

  • 你应该提到这是具有良好定义行为的合法C,而不是"hack"或"未定义行为"的调用. (3认同)
  • 如果有人引用标准会很酷,并解释了为什么原始示例与此答案中声明的严格别名规则不矛盾:http://stackoverflow.com/a/3766967/895245嵌套结构也提到:http:http: //stackoverflow.com/questions/8416417/nested-structs-and-strict-aliasing-in-c (3认同)
  • 具有匹配的内存布局*不*使根据严格别名规则在结构指针之间进行转换是合法的。在 CPython 的对象实现中,实际上有一个 PEP 来解决这个问题:https://www.python.org/dev/peps/pep-3123/。显然,这些演员表看起来很合理,而且几乎总是有效,但 UB 就是 UB。 (3认同)
  • 聚会很晚,但我也同意这个答案是误导性的 - 两个不相关的结构具有相同的布局可能**不会**彼此别名. (2认同)
  • @OliverCharlesworth:对于 Dennis Ritchie 发明的语言以及绝大多数编译器可以配置为处理的方言,答案都是正确的。该标准将这种能力降级为不需要实现支持的“流行扩展”的状态,但没有判断省略它的方言是否应该被认为适用于任何特定目的。 (2认同)

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)

  • “但是指向指针的指针强制转换不执行任何转换(与 int 到 float 的方式相同)。这只是对它们指向的位的重新解释(所有这些都是为了编译器的利益)”这是错误的。指向不同类型的指针可以有不同的表示形式,它们甚至不必具有相同的大小。http://c-faq.com/null/machexamp.html (2认同)

Sec*_*ure 10

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

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

  • AFAIK,此答案正确,并且(当前)接受的答案错误。另请参阅/sf/ask/3339740981/ (2认同)