使用 header 实现通用结构

Implementation of generic struct with header

我对此有点困惑。 练习很长,所以我希望我写下了与我的问题相关的所有内容。 我有一个给定的 header 文件(部分):

typedef void *(*copy_element)(const void *);
typedef void *(*free_element)(void **);

typedef struct group {
    size_t group_size;
    void **data;
    copy_element copy_element_func;
    free_element free_element_func;
} group;

group *group_alloc(copy_element copy_element_func, free_element free_element_func);
void group_free(group **p);
int add(group *group, const void *value);

我需要将 group.c 实现为通用结构。 我的问题是如何为 **data 实现添加和分配函数? 对于已知类型,我会根据类型的大小使用 malloc 和 realloc,但在这里我不确定该怎么做。

group *group_alloc() {
    group *p = malloc(sizeof(group))
    if(p == NULL) {
        //
    }
    p->group_size = 0;

    void **ptr = malloc(sizeof(void*));
    p->data = ptr;
    return p;
}

在练习中,组应包含一个动态数组值。

谢谢!

根据您的描述,我认为您应该执行以下操作:

group_size成员持有组内数据条数

data 成员是一个指针数组,指向添加到组中的对象。

使用这种结构和函数定义的组合,除了将指向对象的指针添加到组中的 data 数组之外,您无能为力。

group_alloc 函数中,您只需为 group 对象本身分配内存并初始化其成员:

group *group_alloc(copy_element copy_element_func, free_element free_element_func){
    group *ret = malloc(sizeof(*ret));
    if(ret == NULL){
        return(NULL);
    }
    ret->group_size = 0; //initially the group holds no pointers to objects
    ret->data = NULL;
    ret->free_element_func = free_element_func;
    ret->copy_element_func = copy_element_func;
    return(ret);
}

添加对象时调用add函数。 调用者传递应存储对象的 group 和指向该对象的指针。您必须为 data 数组中的另一个指针创建 space:

int add(group *group, const void *value){
    void **newData = realloc(group->data, (group->data_size + 1) * sizeof(*group->data)); //Grow the data array by one to store an additional pointer. 
    //realloc may return NULL, in that case an error occured, but you don't want to overwrite your existing data pointer
    if(newData == NULL){
        return(-1); //return -1 to indicate an error occured
    }
    group->data = newData;
    group->data[group->data_size] = value; //store the pointer to the object in the last array field
    group->data_size++; //increment data_size
    return(0); //return 0 to indicate success
}

我有点质疑这种构造的有用性,因为您可以将指向任意数据的指针添加到组中,关于哪种数据以及以后是否必须释放它的信息为零。您可以添加一堆指向静态对象的指针和一堆指向动态分配对象的指针,您将不知道必须释放哪些对象。

事实上,您不应该关心元素的大小和类型,因为调用者应提供 2 个函数来处理这些元素的复制和释放,因此您可以group 函数级别 将它们作为完全不透明的指针处理。

这是一个可能的实现。此代码还包含一个小演示,展示如何处理空终止字符串:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef void *(*copy_element)(const void *);
typedef void *(*free_element)(void **);

typedef struct group {
    size_t group_size;
    void **data;
    copy_element copy_element_func;
    free_element free_element_func;
} group;

group *group_alloc(copy_element copy_element_func, free_element free_element_func);
void group_free(group **p);
int add(group *group, const void *value);

/***
Allocate a new group that will use the 2 provided functions.
The group will be initially empty
*/
group *group_alloc(copy_element copy_element_func, free_element free_element_func) {
    group * g = malloc(sizeof(*g));
    g->group_size = 0;
    g->data = NULL;
    g->copy_element_func = copy_element_func;
    g->free_element_func = free_element_func;
    return g;
}

/*********
* Add a new element to a group.
* Will use the copy_element_func member to build a copy of the element
* This implementation returns the number of elements in the group
*/
int add(group *group, const void *value) {
    size_t sz = group->group_size + 1;  // do not change anything on alloc error
    void **data = realloc(group->data, sz * sizeof(void *));
    if (data == NULL) {  // allocation error
        return 0;
    }
    // use copy_element_func to build a copy of the element
    data[sz - 1] = group->copy_element_func(value);
    group->group_size = sz;
    group->data = data;
    return (int) sz;
}

/******************
* Free a group.
* First free all elements of the group (using the free_element_func member)
* and then the group itself
*/
void group_free(group **p) {
    group *g = *p;
    if (g != NULL) {
        for (int i = 0; i < g->group_size; i++) {
            // again use free_element_func that should be able to free an element
            g->free_element_func(g->data + i);
        }
        free(g);
    }
    *p = NULL;
}

// Example functions for null terminated strings
void * copy_string(const void *input) {
    return strdup(input);
}

void * free_string(void **str) {
    free(*str);
    *str = NULL;
    return *str;
}

// demo code
int main() {
    group *g = group_alloc(&copy_string, &free_string);
    int i = add(g, "foo");
    printf("%d\n", i);   // should display 1
    i = add(g, "bar");
    printf("%d\n", i);   // should display 2
    for (i = 0; i < g->group_size; i++) {
        printf("%s\n", ((char **)g->data)[i]);  // should display foo then bar
    }
    group_free(&g);
    printf("%p\n", g);   // should display a NULL pointer
    return 0;
}

免责声明:此代码盲目地假定 strdup 函数的可用性,而它是可选的并且不会测试分配错误...