【问题标题】:Allocating and freeing memory inside loop in C [closed]在C中的循环内分配和释放内存[关闭]
【发布时间】:2019-08-28 17:22:55
【问题描述】:

我有一个关于如何在 C 中创建临时字符串的问题。我的意思是我想在每个迭代步骤中创建一个字符串,并在变量不再有用后释放它。 我见过与这个类似的问题,但它们有很大的不同。

所以现在我有类似的东西:

for (int i = 0; i < array_size; i++) {
    //aray1 and array2 are arrays of strings
    char* temporary_value = make_hash(array1[i], array2[i], size[i]);
    if (is_valid(temporary_value)) {
       //Code, that doesn't interferate in memory, but uses temporary_value - mostly just compare to it
    }
    free(temporary_value);
 }

make_hash 根据 size[i] 分配内存。

但感觉很不对劲,有时会返回段错误。 我改进的想法是:

  • 制作字符串数组并在循环后释放它
  • 将“make_hash”代码放入for循环中,并在迭代期间重新分配内存,并在for循环之后释放temporary_value

但这些解决方案似乎也很糟糕。您将如何处理此类问题?

【问题讨论】:

  • 您能知道在执行此循环时作为参数传递给make_hash 的最大可能大小吗?
  • 感谢大家的宝贵时间,尤其是@dedecos,因为数组不是那么大,我决定再迭代一次并获得最大可能的大小,在循环之前为变量分配内存并释放它之后:)
  • 希望我能帮上忙(:你想让我创建一个答案还是你已经知道了?
  • 已经知道了,谢谢:)
  • 如果性能很重要,malloc/free 每次循环可能会很浪费。

标签: c string memory-leaks


【解决方案1】:

当函数返回已知大小的对象时,让调用者处理分配通常比函数本身更好,调用者通常知道哪种分配是最好的(自动、静态、堆等)。只需在调用函数时将指针传递到您想要结果的位置即可。

哈希函数通常返回固定大小的哈希,所以我会这样做:

for (int i = 0; i < array_size; i++) {
    char buffer[HASH_SIZE];

    /*
       `make_hash` writes result into `buffer` and returns `buffer` on success, or
        `NULL` on error
    */
    char* temporary_value = make_hash(buffer, array1[i], array2[i], size[i]);
    if (is_valid(temporary_value)) {
       //Code, that doesn't interferate in memory, but uses temporary_value - mostly just compare to it
    }
 }

如果您的哈希函数没有返回一个固定大小的哈希值,并且您希望realloc 您的缓冲区,然后传递一个指向您缓冲区的指针的指针,一起指向保存缓冲区大小的变量的指针:

make_hash(char **buffer, size_t *buffer_size, const char *str1, const char *str2, size_t s)
{
   size_t new_size = .....;
   if (new_size > *buffer_size)
     {
        char *tmp = realloc(*buffer, new_size);
        if (!tmp)
          return NULL;
        *buffer = tmp;
        *buffer_size = new_size;
     }
   /* 
      Calculate hash, and store it wherever `b` is pointing
    */
    char *b = *buffer;
    .......

    return b; /* or `NULL` on error */
}


char *buffer = NULL;
size_t buffer_size = 0;

for (int i = 0; i < array_size; i++) {

    char* temporary_value = make_hash(&buffer, &buffer_size, array1[i], array2[i], size[i]);
    if (is_valid(temporary_value)) {
       //Code, that doesn't interferate in memory, but uses temporary_value - mostly just compare to it
    }
 }
 free(buffer);

如果您有一种计算哈希大小的廉价方法,无需调用make_hash(),您也可以使用第一个解决方案以及可变长度数组:

for (int i = 0; i < array_size; i++) {
    size_t buffer_size = hash_size(.....);

    char buffer[buffer_size];

    /*
       `make_hash` writes result into `buffer` and returns `buffer` on success, or
        `NULL` on error
    */
    char* temporary_value = make_hash(buffer, array1[i], array2[i], size[i]);
    if (is_valid(temporary_value)) {
       //Code, that doesn't interferate in memory, but uses temporary_value - mostly just compare to it
    }
 }

【讨论】:

    【解决方案2】:

    我将如何处理这不是在循环中释放。 malloc/free 通常是非常昂贵的 system 调用,如果你知道你将不得不再次调用 malloc,你不想这样做。

    执行此操作的正确方法是 malloc 一次,然后在后续调用中使用相同的内存块重新分配,然后在循环之外释放。

    【讨论】:

    • 吹毛求疵:malloc/free 通常不是系统调用,而是库函数
    • @HAL9000 谢谢你的捕获,已修复。
    • malloc/free 并没有那么昂贵——通常比系统调用便宜得多。如果大小变化很大, realloc 也可能比 free+malloc 更昂贵。与往常一样,您应该在过分担心性能之前争取清晰,并在担心优化错误之前对代码进行基准测试以找到实际瓶颈。
    【解决方案3】:

    分段错误可能来自多种因素,数组的长度、array1、array2 和大小是否相同。您也在释放内存而不检查它是否已分配。

    我宁愿有这样的东西。

    if (temporary_value != NULL) {
        free(temporary_value);
    }
    

    用 C 编码已经很久了,但这应该有助于排除故障。

    【讨论】:

    • 检查参数是否为NULL 是没有意义的,就好像NULL 被传递给free 一样,它什么也不做。但是freeing 已经释放的指针具有未定义的行为(因此这可能是段错误的原因)。
    猜你喜欢
    • 2016-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-20
    • 2012-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多