使用 free() 释放内存导致崩溃
using free() to free memory cause crash
我正在尝试创建一个小型库来处理字符串,因为在 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
/编辑:输出字符串确实是预期的,这里唯一的问题是释放这个旧指针以防止内存泄漏。结束/
我尝试使用 gdb 看看我是否可以调试任何东西,但一如既往,调试器只在我找不到崩溃位置的情况下才有用,永远无法找出问题。
但是无论如何,如果有人愿意指出我的错误并进一步详细解释为什么它是错误的,我认为这将提高我在这种情况下对指针的理解。
您的代码在很多地方无效:
- 尺寸使用
size_t
String *str = calloc(1, sizeof(unsigned int) + sizeof(chr));
它可能没有为结构分配足够的 space,因为它对填充一无所知
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;
}
这就是你的错误:
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->string
。我明白了,这可以防止堆碎片化,但也会使操作复杂化。
在 str->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] = '[=16=]';
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));
我认为 ptr 变量根本不会泄漏内存。
因为它只是一个指向实际字符串开头的指针,并且它被放置在堆栈而不是堆上,因为单个指针只是一个整数,如果您在函数中声明它们,它们也会自行释放。
如果有内存泄漏,它可能来自这里:
str->string = calloc(1, final_size * sizeof(char));
因为你在堆上为你的 str 结构分配新内存而不释放之前存储在那里的内容。
所以你可以在为连接的字符串分配内存之前尝试 free(str);
。
您正在分配内存并从 calloc() 获取指向它的指针,但未连接字符串的内存仍在堆中,您只是不再有指向它的指针。
我正在尝试创建一个小型库来处理字符串,因为在 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
/编辑:输出字符串确实是预期的,这里唯一的问题是释放这个旧指针以防止内存泄漏。结束/
我尝试使用 gdb 看看我是否可以调试任何东西,但一如既往,调试器只在我找不到崩溃位置的情况下才有用,永远无法找出问题。
但是无论如何,如果有人愿意指出我的错误并进一步详细解释为什么它是错误的,我认为这将提高我在这种情况下对指针的理解。
您的代码在很多地方无效:
- 尺寸使用
size_t
String *str = calloc(1, sizeof(unsigned int) + sizeof(chr));
它可能没有为结构分配足够的 space,因为它对填充一无所知
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;
}
这就是你的错误:
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->string
。我明白了,这可以防止堆碎片化,但也会使操作复杂化。
在 str->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] = '[=16=]';
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));
我认为 ptr 变量根本不会泄漏内存。
因为它只是一个指向实际字符串开头的指针,并且它被放置在堆栈而不是堆上,因为单个指针只是一个整数,如果您在函数中声明它们,它们也会自行释放。
如果有内存泄漏,它可能来自这里:
str->string = calloc(1, final_size * sizeof(char));
因为你在堆上为你的 str 结构分配新内存而不释放之前存储在那里的内容。
所以你可以在为连接的字符串分配内存之前尝试 free(str);
。
您正在分配内存并从 calloc() 获取指向它的指针,但未连接字符串的内存仍在堆中,您只是不再有指向它的指针。