struct Student{

char* name;

int age;

}

場景:

因為需要動態分配了多個Student的實例(malloc),然後每個實例的name也是由malloc動態分配的

問題一:

所以怎麼才能把所有申請的內存都釋放而不產生內存泄漏?

問題二:

假如不那麼複雜,單單free一個Student的實例(name還沒有動態分配到內存),是不是把age和name這兩個變數都收回了?

問題三:

如果是上面假設的那樣的話(free一個實例後age和name這兩個變數都被清理了)

假如name 動態分配到了內存後,再free Student的實例豈不是造成了內存泄漏!?


場景:

因為需要動態分配了多個Student的實例(malloc),然後每個實例的name也是由malloc動態分配的

不。malloc不能分配任何「實例」。malloc分配的是一塊指定大小的內存。你讓一個指針指向這個內存並且通過這個指針訪問這塊內存,是把這塊內存「當作」這個指針指向的類型的一個或多個實例。name也一樣。

問題一:

所以怎麼才能把所有申請的內存都釋放而不產生內存泄漏?

用完一個釋放一個。這裡,name指向的內存如果還沒free,那麼整個結構體指針指向的內存也不能算用完,對吧。

問題二:

假如不那麼複雜,單單free一個Student的實例(name還沒有動態分配到內存),
是不是把age和name這兩個變數都收回了?

free的是一塊內存,它的大小是malloc時指定的。那時指定了多大就free多大。free之後的內存會在某個時候被malloc再次分配。

問題三:

如果是上面假設的那樣的話(free一個實例後age和name這兩個變數都被清理了)

age和name不是變數,它們是結構體成員,是Student這個結構體裏的一個「位置」。free也不會「清理」任何東西,它只是告訴內存分配機制某段內存已經不再被使用了,現在是沒有被分配的狀態。

假如name 動態分配到了內存後,再free Student的實例豈不是造成了內存泄漏!?

某個Student指針所指向的內存被free後,這塊內存裏的所有位置,當然也包括name位置,就不應該再去讀取了。如果另一塊malloc所分配的內存的地址被保存在name位置裏,也就不能讀取了,當然也就不能free了。但是如果你在其他位置保存了這塊內存的地址,你一樣可以通過讀取那個位置裏保存的地址來free這塊內存。


你好,總結一下你的問題:C語言中誰申請了內存誰就釋放,沒有申請堆內存,是不需要釋放的,2段代碼足可以說明問題。

  • 第一種情況:name指針也申請了堆內存,此時需要釋放name指向的內存後再釋放結構體節點指針

Student *p = (Student*)malloc(sizeof(Student)); // 申請內存:結構體一個節點
if (p == nullptr) {
return 0;
}

p-&>name = (char*)malloc(sizeof(char) * kNameLen); // 申請內存:name 指針指向多大可以使用的內存
p-&>age = 22;
strncpy(p-&>name, "lilie", strlen("lilie"));

// 釋放:先釋放p-&>name
free(p-&>name);
// 再釋放 p
free(p);
p = nullptr;

第二種情況:name指針沒有申請堆內存,我們只需要釋放結構體節點指針指向的內存就可以了。

Student *p = (Student*)malloc(sizeof(Student)); // 申請內存:結構體一個節點
if (p == nullptr) {
return 0;
}

p-&>age = 22;
// 無需釋放p-&>name所指向內存,因為壓根就沒有申請內存,只需要釋放 p
free(p);
p = nullptr;

最後附上手繪圖

個人觀點,不懂可以問我。


你用的C還是C++?

如果是C++,建議不要用malloc/free,可以用new/delete,學習一下構造器和析構器,在你這個場景裡面最適合的是用vector和string,避免裸指針和直接分配動態內存

如果是C的話,你問題二的假設是對的。問題三的話,我們一般會寫兩個函數來創造和刪除這個struct,比如

struct Student* createStudent() {
struct Student *newStudent = malloc(sizeof(struct Student));
newStudent-&>name = malloc(100);
return newStudent;
}

void deleteStudent(struct Student* student) {
free(student-&>name);
free(student);
}

必須在把student free掉之前,把它的name給free掉,或者用一個臨時變數把它的name存下來。

你在使用Student的時候,就要避免手動的創造和刪除,而是用這兩個函數。

但並沒有太好的辦法阻止你直接手動分配,全靠自覺。這也是C語言的特點,你寫什麼代碼就做什麼。問題一也是一樣,全都是手動的,沒有很好的辦法保證內存不泄漏。


策略和責任劃分問題,你只不過想知道哪個函數生產,哪個函數來釋放,何時釋放,何時使用,哪個線程使用,哪個線程釋放問題。


寫Destructor比較好。


很可惜這是C不是Python。當結構體中有指針成員,並且指針成員指向的對象也是malloc/calloc分配出來的(也就是在堆內存上),那麼在釋放內存時,必須先將結構體的指針成員所指向的對象free掉,再free結構體本身。或者先保存結構體的指針成員,free結構體,再free指針成員所指向的對象。

遞歸地,如果結構體中的指針成員所指向的對象是個結構體(在堆上),並且這個結構體中也有指向堆上對象的指針成員,那麼,完全釋放內存的操作必須被妥善地組織。


推薦閱讀:
相關文章