深入理解函式中new和delete的具體過程
本來呢,今天準備做雜湊表和堆排序,結果沒想到卡在雜湊表的鏈地址法上了,果然出來混遲早要還的。當年大一指標這塊沒有好好學,現在就要補回去,碰到一個問題,接二連三冒出來五個小問題,好,花時間把這五個小問題都解決了,那個大問題還是沒有解決...哭哭T_T
說正經的,我要先講一下malloc/new和普通變數的區別。
malloc/new會開闢一塊地址,通常大小自己設定,一般都寫成sizeof(XXX)的形式,開闢出來的這塊地址是在堆區的,也就是說你是開的堆區的記憶體,而不是棧區,因此你需要手動用delete/free釋放掉。因此在函式呼叫中也是如此,如果你在函式中malloc或者new了一塊地址,就算函式結束,這塊地址也不會釋放掉的。而普通變數,比如int就不一樣,int在棧區,函式結束後就會回收釋放掉。
我們來看幾個具體的例子,這些例子都是我費勁腦汁想出來的,超級有代表性,很適合大一新生看,對於深入瞭解單鏈表很有幫助。想轉載的話請私信我喲~
#include <bits/stdc++.h> using namespace std; typedef struct node { int data; struct node *next; }Node; void f1(Node **a) { Node *x,*y,*z; x=new Node[sizeof(Node)]; y=new Node[sizeof(Node)]; z=new Node[sizeof(Node)]; x->data=1; y->data=2; z->data=3; z->next=NULL; y->next=z; x->next=y; *a=x; //nothing } void f2(Node **a) { Node *x,*y,*z; x=new Node[sizeof(Node)]; y=new Node[sizeof(Node)]; z=new Node[sizeof(Node)]; x->data=1; y->data=2; z->data=3; z->next=NULL; y->next=z; x->next=y; *a=x; delete x; } void f3(Node **a) { Node *x,*y,*z; x=new Node[sizeof(Node)]; y=new Node[sizeof(Node)]; z=new Node[sizeof(Node)]; x->data=1; y->data=2; z->data=3; z->next=NULL; y->next=z; x->next=y; *a=x; delete y; } void f4(Node **a) { Node *x,*y,*z; x=new Node[sizeof(Node)]; y=new Node[sizeof(Node)]; z=new Node[sizeof(Node)]; x->data=1; y->data=2; z->data=3; z->next=NULL; y->next=z; x->next=y; *a=x; delete z; } void f5(Node **a) { Node *x,*y,*z; x=new Node[sizeof(Node)]; y=new Node[sizeof(Node)]; z=new Node[sizeof(Node)]; x->data=1; y->data=2; z->data=3; z->next=NULL; y->next=z; x->next=y; *a=x; x=NULL; } void f6(Node **a) { Node *x,*y,*z; x=new Node[sizeof(Node)]; y=new Node[sizeof(Node)]; z=new Node[sizeof(Node)]; x->data=1; y->data=2; z->data=3; z->next=NULL; y->next=z; x->next=y; *a=x; x->data=1997; } void f7(Node **a) { Node *x,*y,*z; x=new Node[sizeof(Node)]; y=new Node[sizeof(Node)]; z=new Node[sizeof(Node)]; x->data=1; y->data=2; z->data=3; z->next=NULL; y->next=z; x->next=y; *a=x; x->next=NULL; } void f8(Node **a) { Node *x,*y,*z; x=new Node[sizeof(Node)]; y=new Node[sizeof(Node)]; z=new Node[sizeof(Node)]; x->data=1; y->data=2; z->data=3; z->next=NULL; y->next=z; x->next=y; *a=x; x=new Node[sizeof(Node)]; delete x; } int main() { Node *a=NULL,*p; f8(&a);//f1~f8 if(a==NULL) printf("a is NULL!\n"); else { printf("a is not NULL!\n"); p=a; while(p) { printf("%d ",p->data); p=p->next; } } }
現在的問題就是,現在有八個函式f1到f8,他們基本一樣,就最後兩三行不一樣,你們主要看最後幾行就行了。
前面幾行是這樣的,先開闢x、y、z三個空間,分別寫入1、2、3,然後串在一起,大概長成這個樣子。
我們先看f1,f1就是通過函式將x的地址賦值給a,對於看不懂為什麼寫成“**a”和“&a”的同學,我建議你看一下我部落格裡面“C”專欄,有一篇名為“當你想通過函式改變指標時,你只能通過指標的指標來改變指標”的文章,你看了就懂了.繼續講,剛才說了,new出來的就算函式結束也不會釋放掉,也就是說,此時a=x,那麼a和x指向同一片單元類似下面這樣
這樣,當我遍歷無頭結點的單鏈表的時候,123都應該正常輸出,如果你不信你就執行一下,當執行f1函式時結果如下
當我執行f2時,我們發現有個delete x,這說明什麼?此時x和a仍然指向同一片記憶體單元吧,我現在吧x釋放掉了,那a是不是就變成野指標了?聰明的你一定猜到程式會報錯,類似下面這個樣子。
是不是很恐怖啊?再看f3,f3是釋放y,這裡的結果和f2一樣,但是機制不一樣,你把y釋放以後x的next就成野指標了,因此出錯
再看f4,f4是釋放z,同理,y的next指標成為野指標了,所以f2,f3,f4結果一樣(我說的一樣是都會出錯,但是出錯顯示的數 是不一樣的)
再看f5,f5是x=NULL,回到剛才,x和a指向同一片記憶體,此時我把x置成NULL,但是x和a毛線關係都沒有x,現在就變成了x是空指標,而a仍然指向剛才那片區域,因此結果和f1一樣。
再看f6,f6是x->data=1997,回到剛才,x和a指向同一片記憶體,x指向的記憶體中的數發生了改變,那麼結果就要相應的改變,這裡還可以理解為(*x).data=1997,這樣就更能理解為什麼傳指標可以改變記憶體中的數而傳值不行。結果如下
再看f7,和f6類似,可以理解為(*x).next=NULL,因此只會輸出1一個數字,因為x的next置NULL了,結果如下
再看f8,f8加了兩行,x=new Node[sizeof(Node)]; 和 delete x; 它的意思是又開闢一段新的記憶體空間,讓x指向它,和f5類似,這裡x做的事情和a無關,x只不過不想和a待在一起了,想指另外一塊記憶體,但這並不影響a的指向。結果如下