我可以有一个 C 例程,它可以防止 free()ing 内存两次吗?
Can I have a C routine, which prevents free()ing the memory twice?
经常在项目中使用复杂的结构,例如,如下所示:
struct opts {
char* server;
char* port;
int protocol;
void* protocol_data;
};
为了释放这样的结构,直到今天我采用了如下套路:
void free_proto1(struct opts* opt);
void free_proto2(struct opts* opt);
void free_opts(struct opts** opt) {
free((*opt)->server);
free((*opt)->port);
if ((*opt)->protocol == PROTOCOL1) {
free_proto1(*opt);
}
else if ((*opt)->protocol == PROTOCOL2) {
free_proto2(*opt);
}
else
free((*opt)->protocol_data);
free(*opt);
*opt = NULL;
}
但是如果我有另一个像struct opts* opts2 = opts1
这样的struct opts
指针,在调用free_opts(&opt1)
之后再调用free_opts(&opt2)
肯定会导致程序崩溃。我知道编码的一个好习惯是避免这样的调用。但是有没有机会,我可以检测到内存已经被释放了?我什至有兴趣查看 Process Control Block
(我认为这是所有程序信息所在的位置)。我可以在执行 free()
'ing 操作之前仔细检查 PCB 结构,这样我就可以避免 free()
'ing 内存两次吗?
释放时将指针设置为已知的无效值 - 通常为 NULL。
那么即使你多次释放 - free
忽略 NULL 指针。
您可以使用计数为 API 的引用,即:向您的结构添加一个 size_t refs
字段,然后添加一个
struct opts* ref_opts(struct opts* opt)
API 这将增加 ref 计数器和 return opt;最后,将 free_opts 重命名为 unref_opts() 并且只有当 refs 字段为 0 时才真正释放你的结构。
这将公开一个已知的 API 以获取结构引用并以非常相似的方式释放它;不使用就是用户的错
不幸的是,C 不支持像 f.e 这样的 smart pointers。 C++ 有。
在C中,你总是要小心不要引起内存泄漏。
总之,你可以f.e。以reference counting的方式提供另一个参数。这样做的缺点是每次调用 free_opts
时都需要将引用指针的数量作为参数传递给分配的内存,并且数量必须固定,但这是一种帮助您解决问题的方法。
仅当所有 个引用已被"pseudo-freed" 时才释放内存。
所有传递的引用指针,除了最后一个,都只是一个空指针,指向的内存实际上不会被释放,直到最后一个引用指针已经过去。
int free_opts (struct opts** opt, int ref) {
static int cnt = 0;
cnt++;
if ( cnt != ref ) {
*opt = NULL;
return 0;
}
free((*opt)->server);
free((*opt)->port);
if ((*opt)->protocol == PROTOCOL1) {
free_proto1(*opt);
}
else if ((*opt)->protocol == PROTOCOL2) {
free_proto2(*opt);
}
else
free((*opt)->protocol_data);
free(*opt);
return 1;
}
内存是否实际释放,由 return 值指示。
@RobertSsupportsMonicaCellio 和@Federico 提出了一些防止内存 free
'ing 两次的可靠方法。但我觉得@vll 提出的建议不应该在评论中丢失,因此我将海报建议总结为答案。
另一种跟踪指针的可靠方法是维护已分配地址的 list
,并且仅当它们在列表中时才释放它们。下面是最简单的实现:
#include "list.h"
static struct list addr_list;
struct opts* create_opts(void) {
struct opts* opts = (struct opts*) calloc(1, sizeof(struct opts));
if (opts == NULL)
return NULL;
/* Initialize the opts{} */
list_append(&addr_list, opts);
return opts;
}
int free_opts(struct opts* opts) {
if (in_list(&addr_list, opts) == false)
return 0;
/* Free the opts{} */
list_remove(&addr_list, opts);
return 1;
}
当然,上面的实现只是简单地拒绝了释放structs
的请求,而structs
并不是使用create_opts()
创建的。一个部分解决方案是使用标志来启用强制清理。但我希望,如果可能的话,有人会给出一些具体的答案。
感谢大家的宝贵建议:)
经常在项目中使用复杂的结构,例如,如下所示:
struct opts {
char* server;
char* port;
int protocol;
void* protocol_data;
};
为了释放这样的结构,直到今天我采用了如下套路:
void free_proto1(struct opts* opt);
void free_proto2(struct opts* opt);
void free_opts(struct opts** opt) {
free((*opt)->server);
free((*opt)->port);
if ((*opt)->protocol == PROTOCOL1) {
free_proto1(*opt);
}
else if ((*opt)->protocol == PROTOCOL2) {
free_proto2(*opt);
}
else
free((*opt)->protocol_data);
free(*opt);
*opt = NULL;
}
但是如果我有另一个像struct opts* opts2 = opts1
这样的struct opts
指针,在调用free_opts(&opt1)
之后再调用free_opts(&opt2)
肯定会导致程序崩溃。我知道编码的一个好习惯是避免这样的调用。但是有没有机会,我可以检测到内存已经被释放了?我什至有兴趣查看 Process Control Block
(我认为这是所有程序信息所在的位置)。我可以在执行 free()
'ing 操作之前仔细检查 PCB 结构,这样我就可以避免 free()
'ing 内存两次吗?
释放时将指针设置为已知的无效值 - 通常为 NULL。
那么即使你多次释放 - free
忽略 NULL 指针。
您可以使用计数为 API 的引用,即:向您的结构添加一个 size_t refs
字段,然后添加一个
struct opts* ref_opts(struct opts* opt)
API 这将增加 ref 计数器和 return opt;最后,将 free_opts 重命名为 unref_opts() 并且只有当 refs 字段为 0 时才真正释放你的结构。
这将公开一个已知的 API 以获取结构引用并以非常相似的方式释放它;不使用就是用户的错
不幸的是,C 不支持像 f.e 这样的 smart pointers。 C++ 有。
在C中,你总是要小心不要引起内存泄漏。
总之,你可以f.e。以reference counting的方式提供另一个参数。这样做的缺点是每次调用 free_opts
时都需要将引用指针的数量作为参数传递给分配的内存,并且数量必须固定,但这是一种帮助您解决问题的方法。
仅当所有 个引用已被"pseudo-freed" 时才释放内存。
所有传递的引用指针,除了最后一个,都只是一个空指针,指向的内存实际上不会被释放,直到最后一个引用指针已经过去。
int free_opts (struct opts** opt, int ref) {
static int cnt = 0;
cnt++;
if ( cnt != ref ) {
*opt = NULL;
return 0;
}
free((*opt)->server);
free((*opt)->port);
if ((*opt)->protocol == PROTOCOL1) {
free_proto1(*opt);
}
else if ((*opt)->protocol == PROTOCOL2) {
free_proto2(*opt);
}
else
free((*opt)->protocol_data);
free(*opt);
return 1;
}
内存是否实际释放,由 return 值指示。
@RobertSsupportsMonicaCellio 和@Federico 提出了一些防止内存 free
'ing 两次的可靠方法。但我觉得@vll 提出的建议不应该在评论中丢失,因此我将海报建议总结为答案。
另一种跟踪指针的可靠方法是维护已分配地址的 list
,并且仅当它们在列表中时才释放它们。下面是最简单的实现:
#include "list.h"
static struct list addr_list;
struct opts* create_opts(void) {
struct opts* opts = (struct opts*) calloc(1, sizeof(struct opts));
if (opts == NULL)
return NULL;
/* Initialize the opts{} */
list_append(&addr_list, opts);
return opts;
}
int free_opts(struct opts* opts) {
if (in_list(&addr_list, opts) == false)
return 0;
/* Free the opts{} */
list_remove(&addr_list, opts);
return 1;
}
当然,上面的实现只是简单地拒绝了释放structs
的请求,而structs
并不是使用create_opts()
创建的。一个部分解决方案是使用标志来启用强制清理。但我希望,如果可能的话,有人会给出一些具体的答案。
感谢大家的宝贵建议:)