如果从函数返回,如何获取结构数组中的元素数? +返回时如何处理内存泄漏?

How can I get the number of elements in a an array of structs if returned from a function? +How to handle memory leaks when returning?

我需要获取已在单独函数中初始化的特定结构数组中的元素数量,我该怎么做?

函数初始化数组:

    Hashtbl* init_hashtbl(){
       Hashtbl* hashtbl;
       hashtbl = calloc(SIZE, sizeof(Hashtbl));
       for(int i = 0; i<SIZE; i++){
           hashtbl[i].subscript = malloc(MAX_LENGTH + 1);
           strcpy(hashtbl[i].subscript, "ZERO\n");

           hashtbl[i].value = malloc(MAX_LENGTH + 1);
           strcpy(hashtbl[i].value, "ZERO\n");
       }
       return hashtbl;
   }

主要功能(这只是我需要的典型用途的示例):

    int main() {
        Hashtbl* nums;
        nums = init_hashtbl();
        insert(nums, "example", "example");
        /*insert returns hashtable same as init_hashtbl but if array is full
        carries out a realloc*/
        printf("%d", /*number of elements*/);

    }

哈希表结构:

    typedef struct Hashtbls{
        char *subscript;
        char *value;
    } Hashtbl;

还有一个问题。在大多数情况下,例如上述函数初始化哈希表的情况,我 return 一些使用了 malloc 或 calloc 的东西。我怎样才能释放它以避免内存泄漏,记住我正在使用头文件并且主要功能只是一个测试驱动程序,用户不应该从他的部分中释放出来?

您有一些选择。

1。 将元素个数传给init_hashtbl()函数:

Hashtbl* init_hashtbl(size_t num_of_elements){
   Hashtbl* hashtbl;
   hashtbl = calloc(num_of_elements, sizeof(Hashtbl));
   for(int i = 0; i<num_of_elements; i++){
       hashtbl[i].subscript = malloc(MAX_LENGTH + 1);
       strcpy(hashtbl[i].subscript, "ZERO\n");

       hashtbl[i].value = malloc(MAX_LENGTH + 1);
       strcpy(hashtbl[i].value, "ZERO\n");
   }
   return hashtbl;

}

int main() {
    Hashtbl* nums;
    nums = init_hashtbl(SIZE);
    ...
}

通过这种方式,您可以控制 table 初始化的元素数量。

2。 传递一个指向 size_t 的指针,让 init 函数写入它

Hashtbl* init_hashtbl(size_t *size){
   Hashtbl* hashtbl;
   hashtbl = calloc(SIZE, sizeof(Hashtbl));
   for(int i = 0; i<SIZE; i++){
       hashtbl[i].subscript = malloc(MAX_LENGTH + 1);
       strcpy(hashtbl[i].subscript, "ZERO\n");

       hashtbl[i].value = malloc(MAX_LENGTH + 1);
       strcpy(hashtbl[i].value, "ZERO\n");
   }
   *size = SIZE; // <-- saving the size for the caller through
                 // pointer
   return hashtbl;

}

int main() {
    Hashtbl* nums;
    size_t table_size
    nums = init_hashtbl(&table_size);
    ...
}

对于所有操作 table 但不调整其大小的函数,它们应该 都得到 size_t 参数。例如你的 loadfromfile1 功能。

Hashtbl* loadfromfile(Hashtbl* hashtbl, size_t size, char *path);

所有调整它大小的函数都应该得到一个指针并改变它,就像你的 insert函数2:

Hashtbl *insert(Hashtbl *hashtbl, size_t *size, const char *subscript, const char *value)
{
    // check that the params are not NULL

    // your code here

    // need to resize
    size_t newsize = size + 1;
    Hashtbl *tmp = realloc(hashtbl, newsize * sizeof *tmp);
    if(tmp == NULL)
    {
        // error handling
        return NULL;
    }

    hashtbl = tmp;
    *size = newsize;

    // here you need to initialize your subscript, values for the
    // new hashtbl entries

    // ...

    return hashtbl;
}

请注意,我更改了函数的签名 insert。现在returns一个 指向 table 的新指针并更新 size 当且仅当 realloc 是 成功。3

调用 insert 就像调用 realloc

Hashtbl *tmp = insert(hashtbl, &size, "example", "example");
if(tmp == NULL)
{
    // could not resize,
    // the unresized table is still in hashtbl
    // and size still the old value
}

hashtbl = tmp;

关于释放内存

我建议你创建一个 destroy 函数(或使用其他名称,这不会 事情)。当你的结构有指向动态分配内存的指针时,它是 最好将它们初始化为 NULL。使用 calloc 是一个很好的方法。

如果遇到错误或者想释放整个堆栈,那么你只有 调用您的 destroy 函数。然后 destroy 函数将释放所有 记忆。例如:

hashtbl_destroy(Hashtbl *hashtbl, size_t size)
{
    for(int i = 0; i < size; ++i)
    {
        free(hashtbl[i].subscript);
        free(hashtbl[i].value);
        free(hashtbl[i]);
    }
}

允许free(NULL),最好初始化你的 在分配内存之前,指针将 NULL,因为以防万一 初始化失败,调用destroy函数即可。


最后一件事:当你设计一个像 Hashtbl 这样的结构时,你有 一些操作 table 的函数,我建议你命名你的 使用前缀的函数,例如 hashtbl_.

hashtbl_create(...);
hashtbl_init(...);
hashtbl_destroy();
hashtbl_insert();
hashtbl_remove();

当您有多个 insert 函数时,很容易避免命名冲突。


注释

1 函数 loadfromfile 出现在这个问题中:

2 当一个函数需要一个指针时,判断它是否是一个指针总是一件好事 您将修改指针指向的数据的内容。 如果是字符串,是否要操作字符串。

void some_func(char *txt);

表明 some_func 可能操纵字符串,例如降低 第一个字母的情况。在那种情况下,最好不要传递字符串 文字:

some_func("This might cause a crash");
char txt[] = "This will not";
some_func(txt);

如果您的函数不打算操作字符串,那么它是 最好声明你的功能是这样的:

void some_other_function(const char *txt);

作为 some_function 的来电者,我知道内容不会改变, 所以

some_other_function("Not a problem");

这就是为什么我在 subscript 中使用 const char * 并在 insert 中使用 value 功能。

3 您还可以使用另一种技术,而不是返回新的 table, 你可以传递一个双指针

int insert(Hashtbl **hashtbl, size_t *size, const char *subscript, const char *value)
{
    // check that the params are not NULL

    // your code here

    // need to resize
    size_t newsize = size + 1;
    Hashtbl *tmp = realloc(*hashtbl, newsize * sizeof *tmp);
    if(tmp == NULL)
    {
        // error handling
        return NULL;
    }

    *hashtbl = tmp;
    *size = newsize;

    // here you need to initialize your subscript, values for the
    // new hashtbl entries

    // ...

    return 1; // 1 on success, 0 on failure
}

if(!insert(&hashtbl, &size, "example", "example"))
{
    // could not resize,
    // the unresized table is still in hashtbl
    // and size still the old value
}