鏈表初始化爲什麼使用二級指針的解釋(指向指針的指針)

引言


在數據結構的學習過程當中,有時候會遇到一些一時沒法理解的問題,深究起來倒是語言的底層的語法機制所限制.
就例如在鏈表的構建中,鏈表的初始化和銷燬爲什麼須要使用一個二級指針,而不是隻須要傳遞一個指針就能夠了,其問題的關鍵就在於c語言的參數傳遞的方式是值傳遞
那麼,這篇文章就來聊一聊在鏈表的初始化中一級指針的傳遞和二級指針的區別.數據結構


一級指針和二級指針的區別


1.前提知識:c語言中參數傳遞的方式是值傳遞和地址傳遞

  • 值傳遞:傳遞的是實參的副本,即形參是一個新開闢的類型同樣,裏面的內容同樣,地址不同的一個變量,主函數和被被調用函數用的是不同的內存空間
  • 地址傳遞:傳遞的是一個地址,實際上主函數和被調用函數共用的是同一塊內存空間,被調用函數依然須要一個新的指針來保存起來這塊地址

2.傳遞一級指針:沒法對原指針指向第地址進行修改

一級指針實例:函數

#include <stdio.h>

#include <stdlib.h>

#define MaxSize 100

typedef int ElemType;

typedef struct SingleNode{

         ElemType data;

         struct SingleNode *next;

}SingleNodeList,*Linklist;

void LinkedListInit(SingleNodeList *head){//用一個指針head接收傳入的地址

    Linklist p;

         if((head=(SingleNodeList *)malloc(sizeof(SingleNodeList)))==NULL){

                   exit(1);//給head分配內存,改變了head指針指向的地址(注意這裏的head只是LinkedListInit的head,不是主函數那個)

         }

         head->next=NULL;

}//這個函數結束後head被銷燬了,主函數的那個head不變;

int LinkedList_PushFront(SingleNodeList *head,ElemType x){//2單鏈表頭插入

         SingleNodeList *q;

         if((q=(struct SingleNode *)malloc(sizeof (struct SingleNode)))==NULL){

                   exit(1);

         }

        q->data=x; q->next=head->next;//頭節點的數據域與指針域賦值

         head->next=q;//頭節點加入鏈表

         return 1;

}

int LinkedList_PopFront(SingleNodeList *head,ElemType *x){//3單鏈表頭刪除

         SingleNodeList *p=head,*q;

         if(p->next==NULL){

                   printf("There is no data in the Linkedlist to delete.\n");

                   *x = -12345;//未成功刪除則在x指向單元賦特定值

                   return 0;

         }

        p=head->next;
        q=p;
        head->next=p->next;
        *x=q->data;
        free(q);
        return *x;//請填寫多行代碼

}

int LinkedListGet_current(SingleNodeList *p,ElemType *x){//4取當前指針指數據

        *x =p->data;

         return 1;

}

int LinkedListUpdata_current(SingleNodeList *p,ElemType x){//5修改當前指針數據

         p->data=x;

         return 1;

}

int LinkedListShow(SingleNodeList *head){//6打印單鏈表

         SingleNodeList *p=head;

         if(p->next==NULL){

                   printf("There is no data in the Linkedlist to print.\n");

                   return 0;

         }

         while(p->next!=NULL){

                   printf("%d ",p->next->data);

                   p=p->next;

         }

         printf("\n");

         return 1;

}

void LinkedListDestroy(SingleNodeList **head){//7釋放鏈表

         SingleNodeList *p=*head,*q;

         while(p!=NULL){

                   q=p;

                   p=p->next;

                   free(q);

         }

         *head=NULL;

}

int LinkedListLength(SingleNodeList *head){//8求單鏈表長度

         SingleNodeList *p=head;

         int size=0;

         while(p->next!=NULL){

                   size++;

                   p=p->next;

         }

         return size;

}

int main(){

         SingleNodeList *head,*p;

         ElemType i,x;

         int switch_num;

         scanf("%d",&switch_num);

         switch(switch_num){

                   case 1:

                            LinkedListInit(head); //傳入指針變量head的地址

                            LinkedList_PushFront(head,1);

                            LinkedList_PushFront(head,3);

                            LinkedList_PushFront(head,2);

                            LinkedListShow(head);

                            break;

       }

       LinkedListDestroy(&head);

       return 0;

}

傳遞流程如圖所示學習

clipboard.png

從圖中能夠看出,main函數中咱們定義了 一個指針head,假設它的地址是0x10010,可是還沒給它初始化,也就是說它存的地址是隨機的,咱們也假設它存的是0x12306spa

在main函數中,咱們把head這個指針做爲參數傳遞進去初始化函數(值傳遞),按照值傳遞的原則,初始化函數首先開闢了一個*head指針,它的地址是0x12345(與main函數的0x10010不同),可是它的內容是是和主函數的head是同樣的,都是指向0x12306這個地址指針

在初始化的過程當中,咱們用malloc函數對初始化函數內的head指針分配內存空間,也就是改變了head指針的值,由未初始化的隨機值0x12306改變成了0x10086code

也就是說,因爲這個head是做用在初始化函數內的,mallo做用的不是主函數的head,初始化函數結束後,這個head指針就被銷燬掉了,主函數中的head不受影響,初始化失敗,而分配了內存不能使用,形成了內存泄漏對象


3. 傳遞二級指針:對指針指向的內容進行操做,head銷燬後無影響

二級指針傳遞實例:blog

#include <stdio.h>

#include <stdlib.h>

#define MaxSize 100

typedef int ElemType;

typedef struct SingleNode{

         ElemType data;

         struct SingleNode *next;

}SingleNodeList,*Linklist;

void LinkedListInit(SingleNodeList **head){//1初始化有頭節點的單鏈表

    Linklist p;

         if((*head=(SingleNodeList *)malloc(sizeof(SingleNodeList)))==NULL){

                   exit(1);

         }

         (*head)->next=NULL;

}

int LinkedList_PushFront(SingleNodeList *head,ElemType x){//2單鏈表頭插入

         SingleNodeList *q;

         if((q=(struct SingleNode *)malloc(sizeof (struct SingleNode)))==NULL){

                   exit(1);

         }

        q->data=x; q->next=head->next;//頭節點的數據域與指針域賦值

         head->next=q;//頭節點加入鏈表

         return 1;

}

int LinkedList_PopFront(SingleNodeList *head,ElemType *x){//3單鏈表頭刪除

         SingleNodeList *p=head,*q;

         if(p->next==NULL){

                   printf("There is no data in the Linkedlist to delete.\n");

                   *x = -12345;//未成功刪除則在x指向單元賦特定值

                   return 0;

         }

        p=head->next;
        q=p;
        head->next=p->next;
        *x=q->data;
        free(q);
        return *x;//請填寫多行代碼

}

int LinkedListGet_current(SingleNodeList *p,ElemType *x){//4取當前指針指數據

        *x =p->data;

         return 1;

}

int LinkedListUpdata_current(SingleNodeList *p,ElemType x){//5修改當前指針數據

         p->data=x;

         return 1;

}

int LinkedListShow(SingleNodeList *head){//6打印單鏈表

         SingleNodeList *p=head;

         if(p->next==NULL){

                   printf("There is no data in the Linkedlist to print.\n");

                   return 0;

         }

         while(p->next!=NULL){

                   printf("%d ",p->next->data);

                   p=p->next;

         }

         printf("\n");

         return 1;

}

void LinkedListDestroy(SingleNodeList **head){//7釋放鏈表

         SingleNodeList *p=*head,*q;

         while(p!=NULL){

                   q=p;

                   p=p->next;

                   free(q);

         }

         *head=NULL;

}

int LinkedListLength(SingleNodeList *head){//8求單鏈表長度

         SingleNodeList *p=head;

         int size=0;

         while(p->next!=NULL){

                   size++;

                   p=p->next;

         }

         return size;

}

int main(){

         SingleNodeList *head,*p;

         ElemType i,x;

         int switch_num;

         scanf("%d",&switch_num);

         switch(switch_num){

                   case 1:

                            LinkedListInit(&head);

                            LinkedList_PushFront(head,1);

                            LinkedList_PushFront(head,3);

                            LinkedList_PushFront(head,2);

                            LinkedListShow(head);

                            break;

       }

       LinkedListDestroy(&head);

       return 0;

}

clipboard.png

如圖所示,若是傳遞的是二級指針就不同了,首先咱們在main函數中的操做是和一級指針差很少,只是傳遞的時候傳遞的不是一個指針變量,而是這個指針變量的地址(地址傳遞),可是在初始化函數中咱們接收這個地址的是用一個二級指針,也就是用一個head指針指向傳遞主函數那個head指針的地址,從而來進行對初始化函數中head指針內容的改變去影響到主函數中的head的指向圖片

如圖所示,咱們在mian函數中傳遞了一個head的地址,也就是0x10010,這個地址是指向0x12306指針的地址
在初始化函數中,咱們用一個另外的head指針接受這塊地址,也就是個二級指針,第一級是0x10010,指向0x12305),第二級是0x12345,指向0x10010ip

在初始化函數中,malloc函數操做的對象是head指針內的內容,也就是0x10010這塊指針(主函數中的head指針),這樣成功改變了主函數中的head指向的地址,而在銷燬初始化函數的head指針的時候,主函數的的head指針不受影響


4. 總結

  • 參數傳遞時,在須要改變指針指向的時候須要傳遞二級指針,例如初始化和銷燬鏈表
  • 一二級指針的圖片對好比下圖

clipboard.png

相關文章
相關標籤/搜索