c에서 포인터를 이중 포인터로 캐스팅하는 이유는 무엇입니까?
C에서 OOP를 읽고 있는데 다음 헤더가 있습니다.
#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 **유형 은 무엇을 의미 합니까? 배열의 배열과 같은가요? 그러나 사용자 정의 클래스 (즉, struct에 대한 포인터 class_t뿐만 아니라 더 많은 "공용"메서드) 를 갖고 싶다면 전체 클래스는 유형 배열이 아닙니다 class_t. 그렇다면 왜 빈 포인터를 이중 포인터로 캐스팅하고 즉시 역 참조합니까? 어떻게 이해해야하나요?
그 진술에 대한 책에서 : * (const struct Class **) p = class;
p는 객체에 대한 새 메모리 영역의 시작을 가리 킵니다. 객체의 시작을 struct class_t에 대한 포인터로 취급하는 p 변환을 강제하고 인수 클래스를이 포인터의 값으로 설정합니다.
답변
에 대한 호출 calloc()은 지정되지 않은 "클래스"유형의 1 개 요소 배열을 할당하는 데 사용됩니다. 여기서 해당 유형의 첫 번째 멤버는 class_t*포인터 가 될 것으로 예상됩니다 (따라서 class->size적어도이어야 sizeof(class_t*)하지만 더 높을 수 있음). calloc()로 malloc()표시되는 추가 데이터 멤버 class->size가 0으로 초기화 되도록하는 대신에 사용되는 경우 memset()가 있습니다. 그렇지 않으면 명시 적이 필요합니다.
이상한 cast + dereference는 코드가 입력 class포인터 class_t*를 할당 된 객체의 첫 번째 멤버에 직접 저장할 수 있도록하는 것 입니다.
이중 포인터를 사용하여 배열에 액세스 할 수 있습니다. 이러한 포인터를 역 참조하면 배열의 첫 번째 요소 주소가 제공됩니다. 이 경우에도 class_t*회원 과 동일한 주소 입니다.
OOP 용어로 메모리에있는 개체의 레이아웃은 일반적으로 개체 클래스의 vtable에 대한 포인터로 시작합니다. 여기에는 클래스의 "가상"메서드에 대한 함수 포인터 목록이 포함되어 있습니다. 클래스가 "파생"되면 자손은 단순히 객체의 vtable 포인터를 새로운 함수 포인터 목록으로 설정함으로써 가상 메서드를 "무시"합니다. 이 OOP 개념은 실제로 C에 존재하지 않지만 C ++의 기본입니다. C에서는 수동으로 구현해야합니다. 이것이이 코드가하는 일입니다.
기본적으로 코드는 할당 된 객체에 대해이 메모리 레이아웃을 할당합니다.
------------ --------------------
무효 * p-> | class_t * | -> | size_t 사이즈 |
------------ --------------------
| ... | | 무효 (* ctor) () |
------------ --------------------
| void (* dtor) () |
--------------------
| void (* clone) () |
--------------------
| void (* 다름) () |
--------------------
동일한 할당을 수행하는 또 다른 방법은보다 쉬운 액세스를 위해 "클래스"유형에 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));
...
}
무슨 뜻이에요
const class_t ** type?
포인터에 대한 포인터입니다. 그것은 그것이 가리키는 것이 무엇이든 실제로 다른 포인터임을 표현합니다. 그리고 두 번째는 유형의 구체적인 객체를 가리 킵니다.이 경우에는class_t
[type] ---> [2nd pointer] ---> [class_t object]
왜 *(const class_t **)p = class;?
이 이상한 구조는 class"class_t 객체"가있는 곳에 배치 하는 것입니다. 자,이 new함수가 어떻게 사용되는지 살펴보면 그렇게 이상하지 않습니다.
은 new사용자 정의 형식 (구조체)의 일반적인 생성자입니다. 그러나 구조가 첫 번째 멤버로 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;생성되고있는 하나의이. 그것은 당신이 무언가를 만드는 동안 무언가를 전달해야한다는 것을 의미 할 것입니다. 그래서 요점이 무엇입니까?
이 유형의 객체를 생성하는 데 사용할 수있는 헤더 의 유형 에 대한 const 포인터를 정의하는 트릭 이 있습니다.
foo 헤더에서 :
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;
위는이 유형의 모든 객체를 생성하기 위해 매트릭스와 함께 사용자 정의 유형을 정의합니다. 유형 class_t이며의 크기 Foo가 기록 된다는 점에 유의하는 것이 중요합니다 .
마지막으로 Foo의 새 객체는 다음과 같이 생성됩니다.
void *f = new(Foo);
는 Foo그러나 유형입니다 class_t하지만 우리는이 원하는 Foo. Foo구조체 의 첫 번째 요소 는에 대한 포인터이므로이 class_t관계를 생성하려면 이중 포인터를 내부에 사용해야합니다.new