为什么要在 c 中将指针转换为双指针?
Why to cast pointer to double pointer in c?
我正在阅读 C 中的 OOP,并且有这个 header:
#ifndef _NEW_H
#define _NEW_H
#include <stdarg.h>
#include <stddef.h>
#include <assert.h>
void *new (const void *type, ...);
void delete (void *item);
typedef struct
{
size_t size;
void *(*ctor)(void *self, va_list *app);
void *(*dtor)(void *self);
void *(*clone)(const void *self);
int (*differ)(const void *self, const void *x);
} class_t;
inline void *new (const void *_class, ...)
{
const class_t *class = _class;
void *p = calloc(1, class->size);
assert(p);
*(const class_t **)p = class; //why would you cast to double pointer, when you immediately dereference it?
if (class->ctor)
{
va_list args;
va_start(args, _class);
p = class->ctor(p, &args);
va_end(args);
}
return p;
}
#endif //_NEW_H
现在我不明白这个表达:
*(const class_t **)p = class;
const class_t **
类型是什么意思?它就像一个数组数组?但是如果我想要自定义 class(也就是说,不仅是指向结构 class_t
的指针,而是更多的“public”方法),整体 class 不是一个类型数组 class_t
。那么,为什么我要将 void 指针转换为双指针并立即取消引用它呢?应该怎么理解?
书中关于那句话的内容:
* (const struct Class **) p = class;
p points to the beginning of the new memory area for the object. We
force a conversion of p which treats the beginning of the object as a
pointer to a struct class_t and set the argument class as the value of
this pointer.
对 calloc()
的调用用于分配未指定“class”类型的 1 个元素的数组,其中该类型的第一个成员应为 class_t*
指针(因此 class->size
必须至少为 sizeof(class_t*)
,但可以更高)。可能会使用 calloc()
而不是 malloc()
,这样 class->size
表示的任何其他数据成员都将是 zero-initialized,否则将需要显式的 memset()
。
怪异的转换+取消引用只是为了让代码可以将输入 class
指针直接存储到该分配对象的第一个 class_t*
成员中。
可以使用 double-pointer 访问数组。取消引用此类指针可为您提供数组中第一个元素的地址。在这种情况下,它恰好与 class_t*
成员的地址相同。
在 OOP 术语中,对象在内存中的布局通常以指向对象 class 的 vtable 的指针开始,其中包含指向 class 的函数指针的列表“虚拟”的方法。当 class 从“派生”时,后代通过简单地将对象的 vtable 指针设置为新的函数指针列表来“覆盖”虚拟方法。 OOP 的这个概念在 C 中并不真正存在,但它是 C++ 的基础。在C中,它必须手动实现,这就是这段代码所做的。
基本上,代码正在为分配的对象分配此内存布局:
------------ --------------------
void *p -> | class_t* | -> | size_t size |
------------ --------------------
| ... | | void (*ctor)() |
------------ --------------------
| void (*dtor)() |
--------------------
| void (*clone)() |
--------------------
| void (*differ)() |
--------------------
完成相同赋值的另一种方法是对“class”类型使用 typedef 以便于访问,例如原始代码等同于:
typedef struct
{
class_t *vtable;
// other data members, if class->size > sizeof(class_t*) ...
} class_info_t;
inline void *new (const void *_class, ...)
{
const class_t *class = _class;
class_info_t *p = (class_info_t*) calloc(1, class->size);
assert(p);
p->vtable = class;
// other data members are implicitly zeroed by calloc() ...
...
}
根本不使用任何 typedef 或转换,memcpy()
可以用来完成同样的事情,例如:
inline void *new (const void *_class, ...)
{
const class_t *class = _class;
void *p = calloc(1, class->size);
assert(p);
memcpy(p, &class, sizeof(class));
...
}
What does it mean const class_t ** type?
它是一个指向指针的指针。它表示它所指向的实际上是另一个指针。而 2nd 指向具体的 object 类型,在这种情况下,class_t
[type] ---> [2nd pointer] ---> [class_t object]
为什么*(const class_t **)p = class;
?
这个奇怪的结构所做的是将 class
放在“class_t object”所在的位置。现在,看看这个 new
函数应该如何使用它并不奇怪。
new
是任何自定义类型(结构)的通用构造函数。然而,对 type 有一个要求,即结构必须包含 指向 class_t 的指针作为第一个成员。这是为了启用多态性,基本上是 C++ 在引擎盖下使用指向 v-table.
的指针所做的事情
现在,如果我要定义自定义类型,我会这样做:
struct Foo {
void *class_t;
// whatever members I need for my type
};
如何使用?
现在,定义了类型后,还有一件事要做。请注意,new
函数作为参数接受一个指针 inline void *new (const void *_class, ...)
,该指针用作正在创建的函数的基础 const class_t *class = _class;
。这有点意味着您需要在创建内容时传递内容 - 那么有什么意义呢?
好吧,有一个技巧可以在 header 中定义一个指向 类型 的 const 指针,可以用来构造 object 的 object类型。
在 foo header:
struct Foo {
void *class_t;
// whatever members I need for my type
};
static const struct class_t _Foo = {
sizeof (struct Foo),
// and other functions such ad ctor, dtro, etc...
};
const void *Foo = &_Foo;
上面定义了一个自定义类型和一个矩阵来创建该类型的所有 object。需要注意的是,它的类型是class_t
,记录的是Foo
的大小
最后,新 object 的 Foo 创建如下:
void *f = new(Foo);
然而 Foo
是 class_t
类型,但我们希望它是 Foo
。 Foo
结构的第一个元素是指向 class_t
的指针,因此为了创建这种关系,需要在 new
内部使用双指针
我正在阅读 C 中的 OOP,并且有这个 header:
#ifndef _NEW_H
#define _NEW_H
#include <stdarg.h>
#include <stddef.h>
#include <assert.h>
void *new (const void *type, ...);
void delete (void *item);
typedef struct
{
size_t size;
void *(*ctor)(void *self, va_list *app);
void *(*dtor)(void *self);
void *(*clone)(const void *self);
int (*differ)(const void *self, const void *x);
} class_t;
inline void *new (const void *_class, ...)
{
const class_t *class = _class;
void *p = calloc(1, class->size);
assert(p);
*(const class_t **)p = class; //why would you cast to double pointer, when you immediately dereference it?
if (class->ctor)
{
va_list args;
va_start(args, _class);
p = class->ctor(p, &args);
va_end(args);
}
return p;
}
#endif //_NEW_H
现在我不明白这个表达:
*(const class_t **)p = class;
const class_t **
类型是什么意思?它就像一个数组数组?但是如果我想要自定义 class(也就是说,不仅是指向结构 class_t
的指针,而是更多的“public”方法),整体 class 不是一个类型数组 class_t
。那么,为什么我要将 void 指针转换为双指针并立即取消引用它呢?应该怎么理解?
书中关于那句话的内容:
* (const struct Class **) p = class;
p points to the beginning of the new memory area for the object. We force a conversion of p which treats the beginning of the object as a pointer to a struct class_t and set the argument class as the value of this pointer.
对 calloc()
的调用用于分配未指定“class”类型的 1 个元素的数组,其中该类型的第一个成员应为 class_t*
指针(因此 class->size
必须至少为 sizeof(class_t*)
,但可以更高)。可能会使用 calloc()
而不是 malloc()
,这样 class->size
表示的任何其他数据成员都将是 zero-initialized,否则将需要显式的 memset()
。
怪异的转换+取消引用只是为了让代码可以将输入 class
指针直接存储到该分配对象的第一个 class_t*
成员中。
可以使用 double-pointer 访问数组。取消引用此类指针可为您提供数组中第一个元素的地址。在这种情况下,它恰好与 class_t*
成员的地址相同。
在 OOP 术语中,对象在内存中的布局通常以指向对象 class 的 vtable 的指针开始,其中包含指向 class 的函数指针的列表“虚拟”的方法。当 class 从“派生”时,后代通过简单地将对象的 vtable 指针设置为新的函数指针列表来“覆盖”虚拟方法。 OOP 的这个概念在 C 中并不真正存在,但它是 C++ 的基础。在C中,它必须手动实现,这就是这段代码所做的。
基本上,代码正在为分配的对象分配此内存布局:
------------ -------------------- void *p -> | class_t* | -> | size_t size | ------------ -------------------- | ... | | void (*ctor)() | ------------ -------------------- | void (*dtor)() | -------------------- | void (*clone)() | -------------------- | void (*differ)() | --------------------
完成相同赋值的另一种方法是对“class”类型使用 typedef 以便于访问,例如原始代码等同于:
typedef struct
{
class_t *vtable;
// other data members, if class->size > sizeof(class_t*) ...
} class_info_t;
inline void *new (const void *_class, ...)
{
const class_t *class = _class;
class_info_t *p = (class_info_t*) calloc(1, class->size);
assert(p);
p->vtable = class;
// other data members are implicitly zeroed by calloc() ...
...
}
根本不使用任何 typedef 或转换,memcpy()
可以用来完成同样的事情,例如:
inline void *new (const void *_class, ...)
{
const class_t *class = _class;
void *p = calloc(1, class->size);
assert(p);
memcpy(p, &class, sizeof(class));
...
}
What does it mean
const class_t ** type?
它是一个指向指针的指针。它表示它所指向的实际上是另一个指针。而 2nd 指向具体的 object 类型,在这种情况下,class_t
[type] ---> [2nd pointer] ---> [class_t object]
为什么*(const class_t **)p = class;
?
这个奇怪的结构所做的是将 class
放在“class_t object”所在的位置。现在,看看这个 new
函数应该如何使用它并不奇怪。
new
是任何自定义类型(结构)的通用构造函数。然而,对 type 有一个要求,即结构必须包含 指向 class_t 的指针作为第一个成员。这是为了启用多态性,基本上是 C++ 在引擎盖下使用指向 v-table.
现在,如果我要定义自定义类型,我会这样做:
struct Foo {
void *class_t;
// whatever members I need for my type
};
如何使用?
现在,定义了类型后,还有一件事要做。请注意,new
函数作为参数接受一个指针 inline void *new (const void *_class, ...)
,该指针用作正在创建的函数的基础 const class_t *class = _class;
。这有点意味着您需要在创建内容时传递内容 - 那么有什么意义呢?
好吧,有一个技巧可以在 header 中定义一个指向 类型 的 const 指针,可以用来构造 object 的 object类型。
在 foo header:
struct Foo {
void *class_t;
// whatever members I need for my type
};
static const struct class_t _Foo = {
sizeof (struct Foo),
// and other functions such ad ctor, dtro, etc...
};
const void *Foo = &_Foo;
上面定义了一个自定义类型和一个矩阵来创建该类型的所有 object。需要注意的是,它的类型是class_t
,记录的是Foo
的大小
最后,新 object 的 Foo 创建如下:
void *f = new(Foo);
然而 Foo
是 class_t
类型,但我们希望它是 Foo
。 Foo
结构的第一个元素是指向 class_t
的指针,因此为了创建这种关系,需要在 new