2011-05-19 18 views
9

考慮的C代碼:從C以外的函數中解放出來指針

void mycode() { 
    MyType* p = malloc(sizeof(MyType)); 
    /* set the values for p and do some stuff with it */ 
    cleanup(p); 
} 


void cleanup(MyType* pointer) { 
    free(pointer); 
    pointer = NULL; 
} 

我錯在以爲cleanup(p);之後被調用時,p的內容現在應爲空?請問cleanup(MyType* pointer)是否正確釋放內存分配?

我正在編寫我的大學作業,並發現調試器仍然顯示指針有一個內存地址,而不是像我期望的0x0(或NULL)。

我發現C中的內存管理非常複雜(我希望這不僅僅是我)。任何人都可以看到發生的事情?

回答

17

是的,這將正確釋放內存。

pointer裏面的清理函數是局部變量;只爲該函數存儲的本地存儲的值的副本。

這可能添加到您的困惑,但你可以從mycode方法調整可變p的價值,像這樣:

void cleanup(MyType** pointer) { 
    free(*pointer); 
    *pointer = NULL; 
} 

在這種情況下,pointer存儲指針的地址。通過取消引用,您可以更改存儲在該地址的值。

我會注意到,在軟件的同一邏輯「層次」上處理分配和釋放通常是一種很好的做法 - 即不要讓調用者負責分配內存然後將其釋放到函數中。保持一致並保持在同一水平。

10

cleanup將正常免費p,但它不會改變它的價值。 C是一種按值傳遞的語言,所以你不能從被調用的函數中改變調用者的變量。如果你想設置pcleanup,你需要做的是這樣:

void cleanup(MyType **pointer) { 
    free(*pointer); 
    *pointer = NULL; 
} 

,並調用它像:

cleanup(&p); 

你的代碼是有點非慣用的,你能解釋一下有點更好,爲什麼你想寫這個cleanup函數?

7

是:有存儲器由malloc神奇產生的塊(3)。您已將此存儲器的地址(而不是存儲器本身)以任何有意義的方式分配給指針p,該指針是mycode()中的auto變量。

然後,您通過pcleanup(),按值,將複製指針,並使用本地複製到cleanup(),釋放該塊。 cleanup()然後將它自己的指針實例設置爲NULL,但這沒用。一旦功能完成,參數pointer就不復存在。早在mycode()

,你還有指針p拿着一個地址,但現在該塊是免費的名單上,不是非常有用的存儲,直到再次分配。

您可能會注意到,您甚至可以存儲並從*p,回讀,但會發生各種數量的下游損失,因爲此塊內存現在屬於該庫,並且您可能會損壞其數據結構或數據malloc()塊的未來所有者。

仔細閱讀關於C可以給你可變生命的一個抽象的概念,但它更容易以可視化的近乎普遍的(對於編譯語言,反正)實現參數傳遞和局部變量分配的堆棧操作的。它有助於在C課程之前參加大會課程。

+1

+1用於暗示某些程序集會幫助 – 6502 2011-05-19 06:08:48

+0

Heh,「6502」。 (我明白了) – DigitalRoss 2011-05-19 07:10:35

+0

呵呵呵......不,我是認真的。其實我開始了一個長時間的答覆,但後來我決定放棄它,因爲這更多的是關於教學編程已經從自下而上(IMO的最佳方式)轉到自上而下(這不好,基本上是因爲那裏沒有頂級)來充值(即從Java等醜陋的東西開始並且無處可去)。我真的相信指針是非常簡單的,但只有當你牢牢掌握計算機的工作原理時(一個簡單的程序集是IMO的一個很好的起點)。沒有這種基礎編程就會變成一大堆具有奇特屬性的神奇單詞。 – 6502 2011-05-19 09:04:23

3

這不會工作,因爲pointercleanup()是本地的,因此分配它NULL不會被調用函數看到。有兩種常見的解決方法。

  1. 而不是發送清理指針,發送一個指針指針。從而改變cleanup()如下:
void cleanup(MyType** pointer) 
{ 
    free(*pointer); 
    *pointer = NULL; 
} 

,然後就打電話cleanup(&p)

  1. 第二個很常見的選項是使用一個#define宏來釋放內存並清除指針。

如果您使用的是C++再有就是通過定義cleanup()作爲第三種方式:

無效清理(MyType的& *指針) { //你的舊代碼保持不變 }

1

有兩個問題在這裏:

我錯在想,以後 清理(p );被調用, p的內容現在應該是NULL?

是的,這是錯誤的。在調用free之後,指針指向的內存被釋放。這並不意味着指針指向的內容被設置爲NULL。此外,如果您希望指針pmycode中變爲NULL,則不會發生這種情況,因爲您正在將副本p更改爲cleanup。如果你想要pmycode中爲NULL,那麼你需要一個指向cleanup中指針的指針,即清理簽名應該是cleanup(MyType**)

第二個問題:

威爾清理(的MyType *指針)正確 自由分配的內存?

是的,因爲你是在由malloc的內存將被釋放返回的指針做free

1

這不僅僅是你。

cleanup()會妥善清理你的配置,但不會指針指向設置指針NULL(應該恕我直言被視爲獨立於清理。)的數據由指針傳遞給cleanup(),並且是free()編輯正確,但指針本身是通過值傳遞,所以當您將其設置爲NULL時,您隻影響cleanup()函數的本地指針副本,而不是原始指針。

周圍有這樣三種方式:

  1. 使用指針的指針。

    void cleanup(struct MyType **p) { free(*p); *p = NULL; } 
    
  2. 使用宏。

    #define cleanup(p) do { free(p); p = NULL; } while(0) 
    

    或(可能更好):

    void cleanup_func(struct MyType *p) { /* more complicated cleanup */ } 
    #define cleanup(p) do { cleanup_func(p); p = NULL; } while(0) 
    
  3. 離開指針設置爲NULL給調用者的責任。這可以避免不必要的分配和代碼混亂或破損。