2010-04-01 53 views
3

在经历了一些痛苦的经历后,我明白了悬摆指针和双重自由的问题。我正在寻求适当的解决方案摇晃指针和双免费

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 = NULLB现在悬停。

这种情况如何避免/补救?是参考数字唯一可行的解​​决方案?或者,有没有其他的“防御性”方法来释放内存,以防止free_aStruct(B);爆炸?

+1

转移到Java的最好的事情之一是,当我做了这整个CLASS的关注 - 我曾经每天必须处理的事情,完全消失。 – 2010-04-01 20:21:55

+4

@Bill如果您编写包含它们的代码,则只需处理这些问题。 – 2010-04-01 20:30:40

+3

实质上,你在问:“如果我编写不好的代码,是否可以防止由于我的错误代码而发生不好的事情?”。我不认为有任何形式的答案涉及阳光,光线,小马和兔子。 – 2010-04-01 20:44:14

回答

5

在纯C,解决这一问题的最重要的解决方案就是纪律,因为问题的根源就在这里:

B = A; 

制作指针的副本而不更改结构中的任何内容,绕过编译器的任何警告而使用任何内容。你必须使用这样的事情:

B = getref_aStruct(A); 

下一个重要的事情是保持跟踪分配。一些有用的东西是干净的模块化,信息隐藏和干燥 - 不要重复自己。当你使用free_aStruct()函数释放它时,你直接调用calloc()来分配内存。最好使用create_aStruct()来分配它。这将事物集中在一个地方,而不是在整个代码库中抛出内存分配。

这是任何存储跟踪系统构建在此之上一个更好的基础。

+0

感谢您的回应。这似乎是一个明智的做法。我的aStruct实际上是一个递归数据结构,我确实有一个名为node-ownership(self-other)的标志。我有new_aStruct()将所有权标志设置为SELF。 free_aStruct()现在在释放子节点之前检查是否设置了节点所有权标志。如果需要,B = getref_aStruct(A)将是一个明确地转让所有权的理想场所。这样一次只有一个对象拥有子节点。 getref_aStruct()强制一个人执行纪律。再次感谢你的帮助。 Best,Russ – user151410 2010-04-01 21:24:29

1

即使您可以防止free_aStruct(B)被炸毁,如果在您的评论后面的代码中有任何对B的引用,那将会使用已释放的内存,因此可能会被新数据覆盖任何一点。只需“修复”免费通话将仅掩盖潜在的错误。

+0

好点。这是一个大问题。它主要是数字代码,并有标志指示对象是否干净和脏,以​​防止意外访问。但是,最终,由于该主题上的帖子指出,在c中强制执行起来很困难。我的希望是使用这些技术和各种工具valgrind等,以确保没有非法访问。 Russ – user151410 2010-04-02 19:02:00

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); 

除此之外,你是最终的“悬摆指针”负责自己无论是其无意的编码或设计缺陷...

+0

谢谢,这真的有帮助。那正是我所问的。 Russ – user151410 2010-04-02 18:56:39

1

有些技巧可以使用,但底线是你可以在C中严格执行任何操作。相反,我建议在开发过程中加入valgrind(或purify)。另外,一些静态代码分析器可能能够检测到这些问题中的一些。

+0

我正在使用valgrind。这真的有帮助。有没有开源的静态代码分析器,你会推荐?谢谢,拉斯 – user151410 2010-04-02 19:01:10

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上调用astruct_getref()astruct_free(),那么引用计数根本不需要保护 - 上面的简单实现就足够了)。

这就是说,上述原语可以很容易地扩展,以支持到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)。

+0

谢谢caf。这在我的代码中是绝对可行的。你能提供一个锁定的例子吗? Best,Russ – user151410 2010-04-02 18:58:33

+0

@ user151410:我已经更新了我的答案,以添加一些关于锁定的信息。 – caf 2010-04-02 23:47:42

+0

谢谢,这真的有帮助。我准备好尝试引用计数。你能推荐一些描述参考计数的书吗?感谢俄罗斯 – user151410 2010-04-07 00:48:11