線性表初始化int InitList(LinkList *L)、int InitList(LinkList L)及int InitList(LinkList &L)


  • 單鏈表的存儲結構

   typedef struct LNode

{
      int data;
      struct Node * next
}LNode,  *LinkList,*ptr;

LNode     L:  L是結構體LNode實例化的實體可以用.運算符來訪問結構體成員,即L.elem。

LinkList  L:  L是指向定義的LNode結構體的指針,可以用->運算符來訪問結構體成員,即L->elem,而(*L)就是個Node型結構體的實體了,可以用點運算符訪問該結構體成員,即(*L).elem;

LinkList *L:  L是指向定義的LNode結構體指針指針,所以(*L)是指向LNode結構體的指針,可以用->運算符來訪問結構體成員,即(*L)->elem,當然,(**L)就是LNode型結構體的實體了,所以可以用點運算符來訪問結構體成員,即(**L).elem;

在鏈表操作中,我們常常要用鏈表變量作為函數的參數,這時,用LinkList L還是LinkList *L就很值得考慮深究了,一個用不好,函數就會出現邏輯錯誤,其准則是:

1. 如果子函數會改變指針L的值,而你也希望子函數結束調用后保存L的值(因子函數運行結束后,所有形參,含L,會被釋放),那你就要用LinkList *L的形式傳遞參數。這樣,向子函數傳遞的就是指針的地址,結束調用后,自然就可以去改變實參指針變量的值,讓它指向新的實體;
2. 如果子函數只會修改指針所指向的內容,而不會更改指針變量的值(實體的地址),那么用LinkList L就行了。實質是,子函數修改的是形參指針 指向的內容Lnode,注意到 實參指針也指向Lnode,也就是子函數修改的就是實參指向的內容Lnode,形參指針隨着子函數結束消失不影響 子函數的功能,就是要修改實參  后面指向的內容,而不是實參指針的值。

  • 初始化的鏈表T,子函數調用完畢后,L會指向一個空的鏈表,即會改變指針L的值,所以要用*L
  1.  int InitList1(LinkList *L) {

                   *L = (LinkList)malloc(sizeof(Node));

                   if(!(*L)){return 0;}

                   (*L)->next = NULL;

                   return 1;

              }

  • 清空鏈表L,使L重新變為空鏈表,子函數調用完后不會改變指針L的值,只會改變指針L所指向的內容(即L->next的值)

          2. void ClearList(LinkList L) {

                   LinkList p;

                   while(p = L->next)
                         free(p);
           }
  • 銷毀鏈表L,釋放鏈表L申請的內存,使L的值重新變為NULL,所以會改變L的值,得用*L 
  3. void DestroyList(LinkList *L)  { 

       LinkList p; 

       while(p = (*L)->next ) 

                free(p); 

       free(*L); 

      *L = NULL;

   }

  • 主函數中調用  

  void main() 

 { 

     LinkList T = NULL;

     InitList(&T);

     ClearList(T);

                   DestroyList(&T);

               }

  有同學問,老師在未了解前也有同樣疑問鏈表初始化子函數中,形參 LinkList LL本身就已經是指針了,為何初始化還要這里為何還要定義指針的指針 LinkList *L,何必多此一舉呢,我先把初始化代碼改成如下:

          4.  int InitList2(LinkList L) 

              {

                 L = (LinkList)malloc(sizeof(Node));// 其實只是修改指針型形參L的值(它的值是某個地址);也即: 指針型變量L,其值放的是新地址,把傳遞過來 實參的地址 覆蓋了。

                 if(!L){return 0;}

                 L->next = NULL;

                return 1;

             }

           雖然能通過編譯,但是執行的時候卻是一串亂碼,反復思考,得出原因如下,給初學者一些幫助 

           C語言的函數參數是值調用(指針也是變量,只不過其值是地址),一定要記着!!!

           例如:在main函數中有如下代碼

                      int main()

                           {

                               LinkList T;//T只是聲明,並未創建

                               int i;

                               i=InitList1(T);//目的是初始化創建T,正常情況下通過調用子函數InitList(T)即可;此處賦值給i,只是幫我們根據返回的值理解T傳入子函數的值到底是什么

                              printf("初始化L后:%d\n",i);

                          }

正常的話,子函數1執行完,鏈表T應被初始化,打印出i的值應是1,因為InitList 返回1了。這本是子函數應實現的功能,也是正確的。

分析:主函數定義了T,它是個指向 LNode節點型數據指針,當然也是個變量,那么不妨假設它的是系統隨機分配的一地址1000H(理論上指針變量必須引用到某一實體,即指向一實體變量,才有后續).

   如果用第二種初始化方法這是調用InitList2函數,傳入T的值,此時L = 1000H, 接下來就和T沒有任何關系了,這時候給L又指向了一個節點(L的值為該節點的地址),然后節點的指針域有 L->next == NULL,返回1。

   需要注意的是:子函數2確實初始化了一個鏈表L, 但L是形參,隨着子函數2的結束,所有形參,包括L也會隨之消失。

   但T呢,還是剛剛在主函數里定義的一個指針而已,還是那個 隨機分配的的地址(1000H),並沒有真正的指向剛才創建的節點。

 

   在看看第一種初始化方法方法,傳入指針的指針也就是:形參變量L(本身是指針變量)指向實參變量T的地址

   主函數定義了T,它是個指針變量,假設它的為1000H(聲明時隨機分配),而T的地址,假設為500H

   此時調用初始化子函數1,值傳遞,T的地址傳給L,即:L=500H(細心的會發現L和T指向值為 1000H的同一塊內存空間,也即該內容空間有兩個變量名字表示, *L = 1000H。  此時申請節點后,假設該節點地址為2000H, 其地址賦給 *L,然后返回 1。

   回到主函數,在看一下 T 的地址依然為500,但是 *T呢,不錯,已經是2000H了,也就是說此時的指針T真正的指向了一個節點了。

  •  int InitList1(LinkList *L)同int InitList1(LinkList &L),只不過前者用在C環境(&L編譯不過去),后者 引用方式傳遞參數 用在C++環境才行(可以*L);再者,函數體中,前者(*L)->next,后者L->next. 教材上如出現&L引用方式傳遞參數,請用C++編譯環境。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM