1. 程式人生 > >深入理解函式中new和delete的具體過程

深入理解函式中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的指向。結果如下