簡體   English   中英

將一個結構指針轉換為另一個 - C

[英]Casting one struct pointer to another - C

請考慮以下代碼。

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*類型object*

  1. 我想知道這是怎么可能的,因為cons_objectobject是不同的結構。
  2. 做這樣的事情有什么問題嗎?

有什么想法嗎!

這很好,是在C中實現“面向對象”的一種相當常見的技術。因為struct的內存布局在C中定義良好,只要兩個對象共享相同的布局,就可以安全地在兩者之間轉換指針。他們。 也就是說, type成員的偏移量在object結構中與cons_object結構中的cons_object

在這種情況下, type成員告訴API該objectcons_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;

這在很大程度上是相同的,除了你有一個強烈的保證,孩子“類”的記憶布局將與父母相同。 也就是說,如果你向'base' object添加一個成員,它將自動被孩子們接收,你不必手動確保所有結構都是同步的。

要添加到Dean的答案,這里有一些關於指針轉換的內容。 我忘記了這個術語是什么,但是指向指針轉換的指針不執行任何轉換(以與浮點數相同的方式)。 它只是對它們指向的位的重新解釋(所有這些都是為了編譯器的好處)。 “非破壞性轉換”我認為是。 數據不會改變,只是編譯器如何解釋所指向的內容。

例如,
如果ptr是指向object的指針,則編譯器知道存在一個具有特定偏移量的字段,該偏移量的typeenum 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轉換為指向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 *
                        +---+---+---+---+

使用單獨的結構違反了嚴格的別名規則,並且是未定義的行為: http//cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

在Dean的最后一個例子中使用嵌入式結構很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM