[英]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*
。
cons_object
和object
是不同的結構。 有什么想法嗎!
這很好,是在C中實現“面向對象”的一種相當常見的技術。因為struct
的內存布局在C中定義良好,只要兩個對象共享相同的布局,就可以安全地在兩者之間轉換指針。他們。 也就是說, type
成員的偏移量在object
結構中與cons_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;
這在很大程度上是相同的,除了你有一個強烈的保證,孩子“類”的記憶布局將與父母相同。 也就是說,如果你向'base' object
添加一個成員,它將自動被孩子們接收,你不必手動確保所有結構都是同步的。
要添加到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
轉換為指向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.