悬空指针和双重释放
经过一些痛苦的经历,我明白了悬空指针和双重释放的问题。我正在寻求适当的解决方案。
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;
}
}
执行上述操作仅在调用 free_aStruct(A);
时设置 A = NULL
。 B
现在悬空。
如何避免/补救这种情况?引用计数是唯一可行的解决方案吗?或者,是否有其他“防御性”方法来释放内存,以防止 free_aStruct(B);
爆炸?
After some painful experiences, I understand the problem of dangling pointers and double free. I am seeking proper solutions.
aStruct
has a number of fields including other arrays.
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);
Is there any way to write free_aStruct(X)
so that free_aStruct(B)
exits gracefully?
void free_aStruct(aStruct *X) {
if (X ! = NULL) {
if (X->a != NULL) { free(X->a); x->a = NULL; }
free(X); X = NULL;
}
}
Doing the above only sets A = NULL
when free_aStruct(A);
is called. B
is now dangling.
How can this situation be avoided / remedied? Is reference counting the only viable solution? Or, are there other "defensive" approaches to freeing memory, to prevent free_aStruct(B);
from exploding?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在普通 C 中,解决这个问题最重要的方法是纪律,因为问题的根源就在这里:
在不更改结构体中任何内容的情况下制作指针的副本,在没有编译器任何警告的情况下绕过您使用的任何内容。您必须使用这样的东西:
下一个重要的事情是跟踪分配。一些有帮助的事情是干净的模块化、信息隐藏和 DRY——不要重复自己。您可以直接调用 calloc() 来分配内存,同时使用 free_aStruct() 函数来释放内存。最好使用 create_aStruct() 来分配它。这使得事情集中在一个地方,而不是在整个代码库中分配内存。
对于您在此基础上构建的任何内存跟踪系统来说,这都是一个更好的基础。
In plain C, the most important solution to this problem is discipline, because the root of the problem is here:
Making a copy of the pointer without changing anything within your struct, circumventing whatever you use without any warning from the compiler. You have to use something like this:
The next important thing is to keep track of the allocations. Some things that help are clean modularization, information hiding and DRY -- Don't Repeat Yourself. You directly call calloc() to allocate the memory while you use a free_aStruct() function to free it. Better use a create_aStruct() to allocate it. This keeps things centralized and in one place only, instead of throwing memory allocations all over your codebase.
This is a much better base for whatever memory tracking system you build on top of this.
我不认为你可以自动执行此操作,因为 C 将管理内存的责任和负担交给你,因此你有责任确保引用和悬空指针得到照顾!
顺便说一句,上面的
if
检查中有一个拼写错误...使用小写的“x”而不是“X”...当我查看上面的代码时,我的想法是您正在对
aStruct *
类型的指针变量的副本进行释放。我会将其修改为按引用调用...并这样调用它:
除此之外,您最终要自己对“悬空指针”负责,无论是无意的编码还是设计错误...
I do not think you can do this automatically as C places the onus and burden of you to manage the memory and therefore your responsibility to ensure that references and of course dangling pointers are looked after!
By the way, there's a typo blip in the
if
check above ... use of lower case 'x' instead of 'X'...My thinking when I was looking at the above code is that you are doing a free on a copy of a pointer variable of type
aStruct *
. I would modify it to be a call-by-reference instead...And call it like this:
Other than that, you are ultimately responsible for the 'dangling pointers' yourself whether its an unintentional coding or a design fault...
即使您可以防止 free_aStruct(B) 爆炸,如果您的注释后面的代码中有任何对 B 的引用,那么这将使用已释放的内存,因此随时可能会被新数据覆盖。仅“修复”免费调用只会掩盖潜在的错误。
Even if you could prevent the free_aStruct(B) from blowing up, if there's any reference to B in the code behind your comment, that's going to be using memory that's been freed, and so might be overwritten with new data at any point. Just "fixing" the free call will only mask the underlying error.
您可以使用一些技术,但底线是您所做的任何事情都无法在 C 中严格执行。相反,我建议在您的开发过程中合并 valgrind (或 purify)。此外,一些静态代码分析器可能能够检测其中一些问题。
There are techniques you can use but the bottom line is that nothing you do can be strictly enforcable in C. Instead, i recommend incorporating valgrind (or purify) in your development process. Also, some static code analyzers may be able to detect some of these problems.
引用计数实际上并不那么难:(
在多线程环境中,您还可能需要添加锁定)。
那么你的代码将是:
你已经询问了锁定。不幸的是,当涉及到锁定时,没有一刀切的答案 - 这完全取决于您的应用程序中的访问模式。精心的设计和深刻的思考是无可替代的。 (例如,如果您可以保证没有线程会在另一个线程的
aStruct< 上调用
astruct_getref()
或astruct_free()
/code>,那么引用计数根本不需要受到保护 - 上面的简单实现就足够了)。也就是说,上述原语可以轻松扩展以支持对
astruct_getref()
和astruct_free()
函数的并发访问:...但请注意,任何包含指针的变量受并发访问影响的结构也需要自己的锁定(例如,如果您有一个并发访问的全局
aStruct *foo
,则它将需要一个附带的foo_lock
代码>)。Reference counting's really not that hard:
(In a multithreaded environment you will also potentially need to add locking).
Then your code would be:
You've asked about locking. Unfortunately there's no one-size-fits-all answer when it comes to locking - it all depends on what access patterns you have in your application. There's no substitute for careful design and deep thoughts. (For example, if you can guarantee that no thread will be calling
astruct_getref()
orastruct_free()
on another thread'saStruct
, then the reference count doesn't need to be protected at all - the simple implementation above will suffice).That said, the above primitives can easily be extended to support concurrent access to the
astruct_getref()
andastruct_free()
functions:...but note that any variables containing the pointers to the structs that are subject to concurrent access will need their own locking too (for example, if you have a global
aStruct *foo
that is concurrently accessed, it will need an accompanyingfoo_lock
).