1. 程式人生 > >陳正衝老師講c語言之記憶體的申請malloc() 和釋放free()

陳正衝老師講c語言之記憶體的申請malloc() 和釋放free()

1.如何使用 malloc 函式

不要莫名其妙,其實上面這段小小的對話,就是malloc的使用過程。malloc是一個函式,專門用來從堆上分配記憶體。使用malloc函式需要幾個要求:

記憶體分配給誰?
分配多大記憶體?
是否還有足夠記憶體分配?
記憶體的將用來儲存什麼格式的資料,即記憶體用來做什麼?
分配好的記憶體在哪裡?

如果這五點都確定,那記憶體就能分配。下面先看malloc函式的原型:

1 (void *)malloc(int size)

看到了沒有,這裡的返回型別是(void *),這是多巧妙的一個設計啊。

malloc函式的返回值是一個void型別的指標,引數為int型別資料,即申請分配的記憶體大小,單位是byte。記憶體分配成功之後,malloc函式返回這塊記憶體的首地址。你需要一個指標來接收這個地址。但是由於函式的返回值是void *型別的,所以必須強制轉換成你所接收的型別。也就是說,這塊記憶體將要用來儲存什麼型別的資料。比如:

1 char *p = (char *)malloc(100);

在堆上分配了100個位元組記憶體,返回這塊記憶體的首地址,把地址強制轉換成char *型別後賦給char *型別的指標變數p。同時告訴我們這塊記憶體將用來儲存char型別的資料。也就是說你只能通過指標變數p來操作這塊記憶體。這塊記憶體本身並沒有名字,對它的訪問是匿名訪問
  

上面就是使用malloc函式成功分配一塊記憶體的過程。但是,每次你都能分配成功嗎?

不一定。

函式同樣要注意這點:如果所申請的記憶體塊大於目前堆上剩餘記憶體塊(整塊),則記憶體分配會失敗,函式返回NULL。注意這裡說的“堆上剩餘記憶體塊”不是所有剩餘記憶體塊之和,因為malloc函式申請的是連續的一塊記憶體。既然malloc函式申請記憶體有不成功的可能,那我們在使用指向這塊記憶體的指標時,必須用if(NULL!=p)語句來驗證記憶體確實分配成功了。

2. 用 malloc 函式申請 0 位元組記憶體

另外還有一個問題:用malloc函式申請0位元組記憶體會返回NULL指標嗎?
可以測試一下,也可以去查詢關於malloc函式的說明文件。申請0位元組記憶體,函式並不返回NULL,而是返回一個正常的記憶體地址。但是你卻無法使用這塊大小為0的記憶體。這好尺子上的某個刻度,刻度本身並沒有長度,只有某兩個刻度一起才能量出長度。對於這一點一定要小心,因為這時候if(NULL!=p)語句校驗將不起作用。

3.記憶體釋放

既然有分配,那就必須有釋放。不然的話,有限的記憶體總會用光,而沒有釋放的記憶體卻在空閒。與malloc對應的就是free函數了。free函式只有一個引數,就是所要釋放的記憶體塊的首地址。比如上例:

1 free(p);


free函式看上去挺狠的,但它到底作了什麼呢?

其實它就做了一件事:斬斷指標變數與這塊記憶體的關係。

比如上面的例子,我們可以說malloc函式分配的記憶體塊是屬於p的,因為我們對這塊記憶體的訪問都需要通過p來進行。free函式就是把這塊記憶體和p之間的所有關係斬斷。從此p和那塊記憶體之間再無瓜葛。至於指標變數p本身儲存的地址並沒有改變,但是它對這個地址處的那塊記憶體卻已經沒有所有權了。那塊被釋放的記憶體裡面儲存的值也沒有改變,只是再也沒有辦法使用了。

這就是free函式的功能。按照上面的分析,如果對p連續兩次以上使用free函式,肯定會發生錯誤。因為第一使用free函式時,p所屬的記憶體已經被釋放,第二次使用時已經無記憶體可釋放了。關於這點,我(陳正衝老師)上課時讓學生記住的是:一定要一夫一妻制,不然肯定出錯。

malloc兩次只free一次會記憶體洩漏;malloc一次free兩次肯定會出錯。也就是說,在程式中malloc的使用次數一定要和free相等,否則必有錯誤。這種錯誤主要發生在迴圈使用malloc函式時,往往把malloc和free次數弄錯了。

4.記憶體釋放之後

既然使用free函式之後指標變數p本身儲存的地址並沒有改變,那我們就需要重新把p的值變為NULL:

1 p = NULL;

這個NULL就是我們前面所說的“栓野狗的鏈子”。如果你不栓起來遲早會出問題的。比如:

在free(p)之後,你用if(NULL!=p)這樣的校驗語句還能起作用嗎?
例如:

1 2 3 4 5 6 7 8 9 char *p = (char *) malloc(100); strcpy(p, “hello”); free(p); /* p 所指的記憶體被釋放,但是p所指的地址仍然不變*/ if (NULL != p) { /* 沒有起到防錯作用*/ strcpy(p, “world”); /* 出錯*/ }

釋放完塊記憶體之後,沒有把指標置NULL,這個指標就成為了“野指標”,也有書叫“懸垂指標”。這是很危險的,而且也是經常出錯的地方。所以一定要記住一條:free完之後,一定要給指標置NULL

5.記憶體已經被釋放了,但是繼續通過指標來使用

這裡一般有三種情況:


第一種:就是上面所說的,free(p)之後,繼續通過p指標來訪問記憶體。解決的辦法就是給p置NULL。


第二種:函式返回棧記憶體。這是初學者最容易犯的錯誤。比如在函式內部定義了一個數組,卻用return語句返回指向該陣列的指標。解決的辦法就是弄明白棧上變數的生命週期。

第三種:記憶體使用太複雜,弄不清到底哪塊記憶體被釋放,哪塊沒有被釋放。解決的辦法是重新設計程式,改善物件之間的呼叫關係。

來源:http://www.cnblogs.com/haore147/p/3647437.html