詳解C語言中二級指針與鏈表的應用

前言

這篇文章即將解決你看不懂或者不會寫鏈表的基本操作的問題,對於初學者而言,有很多地方肯定是費解的。比如函數的參數列表的多樣化,動態分配內存空間函數malloc等,其實這些知識和指針聯系緊密,尤其是二級指針。那麼開始好好的學習這篇文章吧!

二級指針講解

簡述:其實就是一個指針指向另一個指針的地址。

我們都知道指針指向地址,但是指針自身也是一個變量,當然也可以被二級指針所指向。

語法:形如 int x = 10; int *q = &x; int **p = & q;

那麼這裡的q指針指向x的地址,p指針指向指針q的地址,*q可以得到x的值,*p可以得到q指針本身,**p也可以得到x的值。

代碼示例:

int main(void)
{
    int x = 10; 
    int* q = &x;
    int** p = &q;    
    printf("x 的地址為:    %d\n", &x);
    printf("q 指向的地址為:%d\n", q);
    printf("*p的值為:      %d\n", *p);   //p指向指針q的地址,那麼*p是解引用操作,
                                          //就等於瞭q本身
    printf("x 的值為:     %d\n", x);
    printf("q 存取的值為: %d\n", *q);
    printf("**p的值為:    %d\n", **p);    //**p相當於解引用解瞭兩次,第一次先得到q本身,
                                          //第二次得到q指向地址的值
    return 0;
}

運行結果:

鏈表的應用 

這裡以帶頭結點的雙鏈表為例

定義雙鏈表的結構體

typedef int ElemType;//將整型數據重命名為int
typedef int Status;//整型重命名為Status
 
//雙鏈表的數據結構定義
typedef struct DouNode {
    ElemType data;               //數據域
    struct DouNode* head;        //前驅指針
    struct DouNode* next;        //後繼指針
}DousList, * LinkList;// 結點指針

代碼解釋:

利用typedef對數據類型進行重命名,隻要在後面遇到的 ElemType和 Status都是整型就夠瞭。雙鏈表結構體包含三個部分:數據域、前驅指針、後繼指針,與單鏈表的區別就是多瞭一個前驅指針。然後大括號結束部分也是重命名,此時DousList和DouNode效果一樣,都是結構體名,然後LinkList是指向結點的指針。

具體使用:

LinkList L,L是一個指針,DousList *P,P也是一個指針,屬於兩種創建方式。

創建雙鏈表

使用兩種正確的創建鏈表形式和一種錯誤的形式,對比著記憶創建方法

傳入一級指針

這種方式並不能成功創建

代碼演示:

void CreateDouList(LinkList L, int n)
{
    LinkList  ptr;
    int i;
    L = (LinkList)malloc(sizeof(DousList));    //為頭結點申請空間
    L->next = NULL;
    L->head = NULL;
    L->data = n;//L->data記錄結點的個數
    ptr = L;
    for (i = 0; i < n; i++)
    {
        int value = 0;
        scanf("%d",&value);
        LinkList me = (LinkList)malloc(sizeof(DouNode));
        me->data = value;    //節點數據域
        me->next = NULL;
        me->head = NULL;
        ptr->next = me;     
        me->head = ptr;
        ptr = ptr->next;     //尾插法建表
    }
}

代碼解析: 

這裡的參數列表是 LinkList L 和 整型數據 n,L是傳入的鏈表頭結點指針,n是用來記錄插入數據的個數的,在下面的for循環用做循環的次數。接下來使用malloc函數為L鏈表分配內存空間,malloc需要用指針來接收,左邊的括號是分配的指針類型,右邊的括號是分配的內存空間大小。分配空間完成之後初始化前驅和後繼指針為空,數據域data記錄數據的個數。ptr指針初始等於L指針,接下來進入n次循環,創建待插入結點指針me並進行分配內存空間和初始化,最後三行代碼進行尾插法建立鏈表:

尾插法:

先讓ptr的後繼指針指向me,然後me的head指針指向ptr,這就相當於在鏈表頭把me結點插入鏈表,然後ptr指向這個插入的新結點,這就保證瞭每次插入的結點都在上一個插入的結點之後。

但是這樣真的在鏈表中插入數據瞭嗎 ,來看看調試結果:

進入遍歷的程序時,讓創建的ptr指針指向L鏈表的後繼,立馬就出現瞭空指針異常,但是上面明明插入數據瞭,原因是什麼呢? 很明顯,這裡的鏈表L並未完成插入數據。這是因為我們在創建鏈表的函數裡傳入的隻是鏈表的指針L,那麼在函數裡這個指針隻是一個副本,在這裡給他增大內存空間並不會影響到實參鏈表,這和普通數據類型的值傳遞和地址傳遞的情況一致。

我們利用傳入指針地址來解決這個問題,兩個方法:指針的引用和二級指針

傳入指針的引用

函數的實現部分完全不用修改,隻要形參列表加上一個引用符"&"即可。

void CreateDouList(LinkList &L, int n);

查看調試結果:

同樣的調試方法,傳入指針的引用之後可以清晰的看到L的data等於5,也就是存瞭五個數據,然後對於的後繼結點的值都和尾插的結果一致,最後一個結點的後繼指針正好指向NULL,完全符合我們的設計的代碼。

傳入指針的引用之後,函數裡鏈表的空間變化會導致實參裡的鏈表空間變化,這樣做才能使插入操作完成,將結點插入到鏈表內。

傳入二級指針

這個和指針的引用原理一樣,我主要分享給你們使用的形式

註意調用的時候實參要加“&”符,例:CreateDouList(&L,n);

void CreateDouList(LinkList *L, int n)
{
    LinkList  ptr;
    int i;
     *L = (LinkList)malloc(sizeof(DousList));    //為頭結點申請空間
    (*L)->next = NULL;
    (*L)->head = NULL;
    (*L)->data = n;//L->data記錄結點的個數
    ptr = (*L);
    printf("開始插入數據:\n");
    for (i = 0; i < n; i++)
    {
        int value = 0;
        scanf("%d",&value);
        LinkList me = (LinkList)malloc(sizeof(DouNode));
        me->data = value;    //節點數據域
        me->next = NULL;
        me->head = NULL;
        ptr->next = me;     
        me->head = ptr;
        ptr = ptr->next;     //尾插法建表
    }
}

這裡形參列表的參數是 LinkList *L,和DousLIst **L效果一樣,是一個二級指針。如果用到指向鏈表的指針就需要一次接引用操作,寫成(*L)的形式。然後再去分配空間、進行初始化、賦值給鏈表指針ptr等操作,這樣鏈表二級指針L的改變也會使實參的鏈表發生改變,可以查看調試結果。

調試結果:

以上就是詳解C語言中二級指針與鏈表的應用的詳細內容,更多關於C語言 二級指針 鏈表的資料請關註WalkonNet其它相關文章!

推薦閱讀: