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

标签 c function struct memory-leaks element

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

函数初始化数组:

    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;

还有一个问题。在大多数情况下,例如上述函数初始化哈希表的情况,我会返回一些使用了 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);
    ...
}

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

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);
    ...
}

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

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

所有调整它大小的函数都应该得到一个指针并改变它,就像你的 插入函数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 的签名。它现在返回一个 指向表的新指针并更新 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 这样的结构时,你有 一些操作表的函数,我建议你命名你的 使用前缀的函数,例如 hashtbl_

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

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


注释

1 函数 loadfromfile 出现在这个问题中:Why is reading from file function crashing?

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");

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

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

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
}

关于c - 如果从函数返回,如何获取结构数组中的元素数? +返回时如何处理内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48251538/

相关文章:

python - 将 argv 的元素复制到新数组中

python - 根据标题列表创建聚合列

php - 在PHP中如何检查类是否存在?

c - 有没有一种巧妙的方法来执行 strdup() 后跟 strcat()?

c++ - 由于标准测试失败,用于查找点是否在 2D 多边形内的替代测试

C - 如何从文件中获取没有数字、特殊字符(、.?&)的单词?

python - 具有不同条件语句的函数返回

c - 递归混淆

c# - 将复杂的结构(带有内部结构数组)从 C# 传递到 C++

指向常量结构中结构元素的常量指针