aStruct
包含许多字段,包括其他数组。
aStruct *A = NULL, *B = NULL;
A = (aStruct*) calloc(1, sizeof(sStruct));
B = A;
free_aStruct(A);
...
// Bunch of other code in various places.
...
free_aStruct(B);
有没有办法写free_aStruct(X)
以便free_aStruct(B)
优雅地退出?
void free_aStruct(aStruct *X) {
if (X ! = NULL) {
if (X->a != NULL) { free(X->a); x->a = NULL; }
free(X); X = NULL;
}
}
执行上述操作仅在调用A = NULL
时设置free_aStruct(A);
。 B
现在正悬空。
如何避免/纠正这种情况?引用计数是唯一可行的解决方案吗?或者,是否有其他“防御性”方法来释放记忆,以防止free_aStruct(B);
爆炸?
答案 0 :(得分:5)
在简单的C中,这个问题最重要的解决方案是纪律,因为问题的根源在于:
B = A;
制作指针的副本而不更改结构中的任何内容,在没有编译器任何警告的情况下绕过您使用的任何内容。你必须使用这样的东西:
B = getref_aStruct(A);
下一个重要的事情是跟踪分配。有些事情有助于清洁模块化,信息隐藏和干燥 - 不要重复自己。在使用free_aStruct()函数释放它时,直接调用calloc()来分配内存。最好使用create_aStruct()来分配它。这样可以将事物集中在一个地方,而不是在整个代码库中投入内存分配。
对于您在此基础上构建的内存跟踪系统,这是一个更好的基础。
答案 1 :(得分:2)
我不认为你可以自动执行此操作,因为C会给你带来责任和管理内存的负担,因此你有责任确保引用,当然还有悬空指针得到照顾!
void free_aStruct(aStruct *X){ if (X ! = NULL){ if (X->a != NULL){free(X->a); x->a = NULL;} free(X); X = NULL; } }
顺便说一句,上面的if
检查中有一个拼写错误...使用小写'x'而不是'X'......
当我查看上面的代码时,我的想法是你在aStruct *
类型的指针变量的副本上做一个免费的。我会将其修改为引用调用而不是......
void free_aStruct(aStruct **X){ if (*X ! = NULL){ if (*X->a != NULL){ free(*X->a); *X->a = NULL; } free(*X); *X = NULL; } }
并称之为:
free_aStruct(&A);
除此之外,无论是无意的编码还是设计错误,你都要对自己的“悬挂指针”负责......
答案 2 :(得分:1)
即使你可以阻止free_aStruct(B)爆炸,如果在你的评论后面的代码中有任何对B的引用,那将会使用已被释放的内存,因此可能会被任何新数据覆盖点。只是“修复”免费通话只会掩盖潜在的错误。
答案 3 :(得分:1)
您可以使用某些技术,但最重要的是,您所做的任何事情都不能在C中严格执行。相反,我建议在开发过程中加入valgrind(或purify)。此外,一些静态代码分析器可能能够检测到其中一些问题。
答案 4 :(得分:1)
引用计数真的不那么难:
aStruct *astruct_getref(aStruct *m)
{
m->refs++;
return m;
}
aStruct *astruct_new(void)
{
sStruct *new = calloc(1, sizeof *new);
return astruct_getref(new);
}
void astruct_free(aStruct *m)
{
if (--m->refs == 0)
free(m);
}
(在多线程环境中,您还可能需要添加锁定)。
然后你的代码将是:
aStruct *A = NULL, *B = NULL;
A = astruct_new();
B = astruct_getref(A);
astruct_free(A);
...
//bunch of other code in various places.
...
astruct_free(B);
你问过锁定问题。不幸的是,在锁定方面没有一个通用的答案 - 这完全取决于您在应用程序中使用的访问模式。精心设计和深刻思考是无可替代的。 (例如,如果您可以保证没有线程将在另一个线程的astruct_getref()
上调用astruct_free()
或aStruct
,那么引用计数不需要完全受到保护 - 上面的简单实现就足够了。
也就是说,可以轻松扩展上述原语以支持对astruct_getref()
和astruct_free()
函数的并发访问:
aStruct *astruct_getref(aStruct *m)
{
mutex_lock(m->reflock);
m->refs++;
mutex_unlock(m->reflock);
return m;
}
aStruct *astruct_new(void)
{
sStruct *new = calloc(1, sizeof *new);
mutex_init(new->reflock);
return astruct_getref(new);
}
void astruct_free(aStruct *m)
{
int refs;
mutex_lock(m->reflock);
refs = --m->refs;
mutex_unlock(m->reflock);
if (refs == 0)
free(m);
}
...但是请注意,包含指向并发访问的结构的指针的任何变量也需要它们自己的锁定(例如,如果你有一个同时访问的全局aStruct *foo
,它将会需要随附的foo_lock
)。