使用 free() 释放内存导致崩溃

Posted

技术标签:

【中文标题】使用 free() 释放内存导致崩溃【英文标题】:using free() to free memory cause crash 【发布时间】:2022-01-05 20:24:51 【问题描述】:

我正在尝试制作一个小型库来处理字符串,因为在 C 中处理它们异常复杂。

我有一个这样定义的结构:

typedef struct _String

    unsigned int size;
    char *string;
 String;

这很简单,并且允许我动态更改数组大小(前提是我正确使用它)。

我有一个专门用于创建这种结构的函数, 以及使用指向String 的指针释放内存的函数。

String *create_string(char *chr)

    String *str = calloc(1, sizeof(unsigned int) + sizeof(chr));
    str->string = chr;
    str->size = strlen(chr);

    return str;


void destroy_string(String *str)

    free(str);

但无论如何,我在制作这样定义的连接函数时遇到了问题:

bool concat_string_char(String *str, char *chr)

    // No use to continue since the passed String isn't initialized
    if (str->string == NULL) return false;

    // Storing the previous string pointer
    char *ptr = str->string;
    
    // Final size after concat
    int final_size = str->size + strlen(chr);

    // Allocating a new block of memory of size final_size * sizeof(char)
    str->string = calloc(1, final_size * sizeof(char));

    // Append each characters of orignal string
    for (int i = 0; i != str->size; i++)
    
        str->string[i] = ptr[i];
    

    // append each character of chr
    for (int i = 0; i != strlen(chr); i++)
    
        str->string[str->size++] = chr[i];
    

    // Free the memory allocated by the previous string -> Crash
    free(ptr);

    return true;

正如我所评论的,当我在原始字符串使用的指针处释放内存时会发生崩溃。

包括:

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

您可以尝试使用上面的3个功能如下(前提是您评论free()

int main(void)

    String *str = create_string("Original");
    concat_string_char(str, " Concatenated");
    printf("%s\n", str->string);
    destroy_string(str);
    return 0;

回复:https://replit.com/@Mrcubix-Mrcubix/String-test#main.c

/EDIT:输出字符串确实是预期的,这里唯一的问题是释放这个旧指针以不泄漏内存。结束/

我尝试使用 gdb 来查看是否可以调试任何东西,但与往常一样,调试器仅在我找不到崩溃位置的情况下才有用,从不找出问题。

但无论如何,任何人都想指出我的错误并更详细地解释为什么它是错误的,我认为这会提高我在这种情况下对指针的理解。

【问题讨论】:

拥有一个minimal reproducible example 包括原始分配等会很有用,而不仅仅是这部分 concat_string_char 函数中使用之前,您是否在尝试释放的指针上使用了 malloc? 我想看看你是如何创建str的。我认为str-&gt;string 没有正确初始化。 T0maas,String 是使用 calloc 分配并使用字符串初始化的,让我编辑我的线程以添加那段代码。 T0maas 函数按要求添加。 【参考方案1】:

您的代码在很多地方都无效:

    使用size_t 表示尺寸
    String *str = calloc(1, sizeof(unsigned int) + sizeof(chr));

它可能没有为结构分配足够的空间,因为它对填充一无所知

    str->string = chr;

你需要复制它。但是您没有为字符串分配任何内存。赋值不会为其分配内存或复制字符串内容。

concat_string_char 中,您尝试释放未动态分配的指针 - 因此崩溃。

我会以其他方式实现它:

typedef struct String

    size_t size;
    char string[];
 String;


String *create_string(const char * restrict chr)

    size_t len = strlen(chr);
    String *str = malloc(sizeof(*str) + len + 1);
    if(str)
    
        str->size = len;
        memcpy(str -> string, chr, len + 1);
    

    return str;


void destroy_string(String *str)

    free(str);


String *concat_string_char(String *str, char *chr)

    size_t len;
    if(str)
    
        str = realloc(sizeof(*str) + str > size + (len = strlen(chr)) + 1);
        if(str)
        
            strcpy(str -> data + str -> size, chr);
            str -> size += len;
        
    
    return str;

【讨论】:

请考虑添加一些关于您建议的实现的解释,考虑到使用灵活的数组成员而不是指针,OP 或任何其他读者可能不会立即清楚它是如何工作的。跨度> 【参考方案2】:

你错了:

String *create_string(char *chr)

    String *str = calloc(1, sizeof(unsigned int) + sizeof(chr));
    str->string = chr;
    str->size = strlen(chr);

    return str;

第一个问题在这里:

String *str = calloc(1, sizeof(unsigned int) + sizeof(chr));

您正在为整个结构分配内存,包括str-&gt;string。我明白了,这可以防止堆碎片,但也会使操作复杂化。

str-&gt;string 上调用free 将导致分段错误,因为该地址无效。您只能在str 上拨打free

第二:

str->string = chr;

这不是复制字符串,这只是分配指针。那是完全错误的。您必须使用 memcpy 或类似方法进行复制:

memcpy(res->string, value, res->size);

第三:这可能有效:

String *create_string(char *chr)

    String *str = malloc(sizeof(String));
    str->size = strlen(chr);
    str->string = malloc(str->size);
    memcpy(res->string, value, res->size);
    return str;

而且,如果你想添加终止 NULL 字符,试试这个:

void destroy_string(String *str)

    free(str->string);
    free(str);

最后:您没有设置终止 NULL 字符,打印时请记住这一点(例如:使用标准打印功能)。

如果要添加终止符NULL,请将构造函数更改为此。

String *create_string(char *chr)

    String *str = malloc(sizeof(String));
    str->size = strlen(chr);
    str->string = malloc(str->size+1);
    memcpy(res->string, value, res->size);
    str->string[str->size] = '\0';
    return str;

当然你需要在 concat 函数中考虑到这一点。

注意:您可以通过从源字符串中复制空字符来避免第二次赋值,因为 C 中的所有字符串都以 NULL 结尾(感谢 @0___________):

    memcpy(res->string, value, res->size+1);

更新:您错误地使用了calloc

str->string = calloc(1, final_size * sizeof(char));

正确的用法是:

str->string = calloc(final_size, sizeof(char));

【讨论】:

你可能有什么事情,让我快速检查一下 memcpy(res-&gt;string, value, res-&gt;size + 1); 不需要下一个作业【参考方案3】:

我认为 ptr 变量根本没有泄漏内存。 因为它只是一个指向实际字符串开头的指针,它被放置在堆栈上而不是堆上,因为单个指针只是一个整数,如果你在函数中声明它们,它们也会自行释放。 如果有内存泄漏,它可能来自这里:str-&gt;string = calloc(1, final_size * sizeof(char)); 作为您在堆上为您的 str 结构分配新内存而不释放之前存储在那里的内容。 因此,您可以在为连接字符串分配内存之前尝试free(str);。 您正在分配内存并从 calloc() 获取指向它的指针,但未连接字符串的内存仍在堆上,您只是不再有指向它的指针。

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于使用 free() 释放内存导致崩溃的主要内容,如果未能解决你的问题,请参考以下文章

在用free()函数释放指针内存时为何要将其指针置空

C ++释放共享库中动态分配的内存导致崩溃

Linux内存cached释放

buff/cache占用太多内存,如何释放内存?

开发那些事儿:Go加C.free释放内存,编译报错是什么原因?

为啥 free() 并没有真正释放内存?