为什么这里需要一个 double-void 指针?动态 "generic" 数组

Why is a double-void pointer required here? Dynamic "generic" array

我试图实现一种 collections-library 的形式。在学习一门新语言时,我一直这样做,因为它教授了大部分语言细节。

所以,我从一种“通用”动态数组的形式开始。好吧,它并不是真正通用的,因为它只是包含指向实际数据的指针。 不过老实说,我不是很明白,为什么我这里需要一个double void指针。

我的头文件中定义的 Vector 结构(我在头文件中声明了每个方法和#include,但我在这里省略了它以保持代码的可读性。我还省略了一些边界检查)

typedef struct {
    size_t capacity; //the allocated capacity
    size_t length; //the actual length
    void **data; //here I don't fully understand, why I need a double pointer.
} Vector;

这是我实现的一些方法,当我在我的结构中使用单个 void 指针时编译器会报错,所以 void *data 而不是 void **data

#include "utils.h"

const size_t INITIAL_SIZE = 16;

//Creates a new empty vector.
Vector *vec_new(void) {
    printf("sizeof Vector is: %ld", sizeof(Vector));
    Vector *vec = malloc(sizeof(Vector));
    vec->length = 0;
    vec->capacity = INITIAL_SIZE;
    void *data = calloc(INITIAL_SIZE, sizeof(void*));
    if(data == NULL) {
        free(vec->data);
        fprintf(stderr, "Error allocating memory.");
        exit(EXIT_FAILURE);
    }
    vec->data = data;
    return vec;
}

//This method appends the specified value at the end of the vector.
void vec_push(Vector *vec, void *data) {
    if(vec->length == vec->capacity-1) {
        vec_resize(vec);
    }
    vec->data[vec->length] = data;
    vec->length += 1;
}

//gets the value at the specified index or NULL if index is out of bounds.
void *vec_get(Vector *vec, size_t index) {
    return vec->data[index];
}

//Resizes the vector to 1.5x its current capacity.
void vec_resize(Vector *vec) {
    vec->capacity *= 1.5;
    void *data = realloc(vec->data, sizeof(void*) * vec->capacity);
    if(data == NULL) {
        free(vec->data);
        fprintf(stderr, "Error allocating memory.");
        exit(EXIT_FAILURE);
    }
    vec->data = data;
}

这似乎是魔法发生的地方,我还不明白:

void *data = malloc(...);
vec->data = data;

Malloc/calloc return 一个空指针,所以我要么必须声明一个实际类型,要么只使用 returned 空指针。所以第一行很清楚。 vec->data 是,根据我的理解,我没有在结构定义中使用相当于 (*vec).data 的双指针。所以基本上这一行应该将一个 void 指针分配给一个 void 指针。

也许有人可以用简单的术语向我解释一下,为什么这里只有一个 void 指针是不够的,或者我可能会误解的地方。

如果你有一个类型的指针

T *p1;

其中 T 是某种类型说明符,例如 void 然后指向该指针的指针将被声明为

T **p2 = &p1.

在这个calloc的调用中

calloc(INITIAL_SIZE, sizeof(void*))

您将分配一个类型为 void * 的指针数组。函数 returns 指向分配数组第一个元素的指针。所以你需要写

void **data = calloc(INITIAL_SIZE, sizeof(void*));

为了更清楚,我们假设您需要动态分配一个整数数组。在这种情况下,您将写

int *data = calloc( INITIAL_SIZE, sizeof( int ) );

所以取消引用指针 data 就像 *data 你会得到一个 int 类型的对象,更准确地说是分配数组的第一个元素。

当数组元素的类型为 void * 然后取消引用指针 data 就像 *data 你必须得到一个类型为 void * 的指针(第一个分配数组的元素)。因此,为了使操作正确,指针 data 的类型应为 void **.

But to be honest, I don't fully understand, why I need a double void pointer here.

先介绍一些背景知识 - 也许您已经知道:

someType * 类型的指针是指向 someType 类型变量的指针 指向 [=] 类型变量数组的指针14=].

someType ** 类型的指针是指向 someType * 类型变量的指针 - 这意味着:指向 someType 类型变量的指针。

void * 类型的指针是指向任何东西的指针;因为编译器不知道这个指针指向什么样的元素,所以不可能直接访问这样的元素。

相对于此,可知void **类型的指针指向什么变量: 指向void *.

类型的变量

为什么这个位置需要void**

关键是这几行:

vec->data[vec->length] = data;
...
return vec->data[index];

在这些行中,代码访问 vec->data 指向的数据。因此,vec->data 不能是 void * 而必须是 xxx *xxx 是指针 vec->data 指向的数据类型。因为 vec->data 指向类型 void * 的指针,xxxvoid * 所以 xxx *void **.

vec->data = data;

您的观察是正确的:vec->data 属于 void ** 类型,data 属于 void *.

类型

原因是malloc()returns有一些内存,编译器不知道这块内存中存放的是哪种数据。所以 malloc() 返回的值是 void * 而不是 void **.

在汽车行业中,您会使用如下所示的显式指针转换:

vec->data = (void **)data;

表达式(xxx *)y告诉编译器指针y指向一些xxx类型的数据。所以 (void **) 告诉编译器指针指向 void *.

类型的元素

但是,在桌面应用程序中,您通常不会编写 (void **)