2010-04-01 92 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