一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

第三十講 正確的List(指針版)

 昵稱29398856 2016-01-21

       昨天我們完成了一個數(shù)組版的List,但是因為數(shù)組的限制,導(dǎo)致那個實現(xiàn)不能收放自如,為了解決這個問題,我們引入指針版本的List,我們還是先來看看接口:
-----------------------------------------
//my_list.h
#ifndef _MY_LIST_H_
#define _MY_LIST_H_

typedef struct TelPhone{
            char Name[20];
            char TelNumber[20];
}iTem;

/*
typedef struct node{
                iTem item;
                node* next;
}Node;*/

struct list{
                iTem item;
                list* next;
};

typedef list* List;

void InitializeList (List * plist);
bool ListIsEmpty (const List * plist);
bool ListisFull (const List * plist);
unsigned int ListItemCount (const List * plist);
bool AddItem (iTem item, List *plist);
void ShowListItem(const List *plist);
void FreetheList (List *plist);

#endif
---------------------------------------
       大家應(yīng)該注意到了,接口還是我們熟悉的接口,我們只是稍微把聲明新類型的時候稍作了下修改,所以對于這個接口,我們就不多說,如果還有不了解的可以回頭去看看第二十七講我們關(guān)于接口的聲明的那一章,不過雖然說沒啥好說,有一點還是值得給大家提一提:
-------------------------------------

typedef list* List;

--------------------------------------

       大家可能會覺得奇怪,為什么會是這樣的呢?如果這樣取個外號,那么我們下面的List*想要表達(dá)的豈不是list **了嗎?是啊,我們要的就是這種效果,指針的指針,指針的指針很多時候又可以用來表示二維數(shù)組:

-------------------------------------

int   a[n][m];//二維數(shù)組

int   **a;//指針的指針

---------------------------------------

       上面這兩種聲明方式在特定的情況下是可以相互表示。如果說大家能夠很好的理解這個,那么對于我們的List*是不是也很好的理解呢?List*plist等價于list** plist,可以這么來理解,plist是一個指向list指針的指針,既是*plist的指針,而*plist是指向list的指針,就是我們需要的指針,這樣聲明的好處是我們很容讓*plist成為一個空殼,也就是好初始化。說到這里,有人會問,既然是一個指向指針的指針,我們可不可以這么來聲明呢:

----------------------------------

typedef struct TelPhone{
            char Name[20];
            char TelNumber[20];
}iTem;

typedef struct node{
                iTem item;
                node* next;
}Node;

struct list{
                Node **head;
                list* next;
};

-----------------------------------------

         這樣我們同樣可以用下面的方式去初始化:

-------------------------------------------

*(plist->head) = NULL;

-------------------------------------------

        這看上去似乎是沒錯的,而且還可以瞞天過海,當(dāng)然瞞的是編譯器,所以如果這樣寫,編譯不報錯,但是運行時報錯了,這是為什么呢?這個問題大概只有大家學(xué)完C++的動態(tài)類型識別的時候才會得到答案,不過這可能會是很久以后的事?,F(xiàn)在我們回到正題上來,既然我們不能這樣寫,那么我們還是規(guī)規(guī)矩矩的這樣初始化:

----------------------------------

*plist = NULL;

----------------------------------

       那么*plist表示什么呢?我們只知道他是一個指向list的指針,里面包含了我們需要iTem和next,但是我們可能有些朋友并不知道他實際上就我們要實現(xiàn)的list的首地址,所以這就是指針的方便之處,我們得到了一塊內(nèi)存的指針便得到了這塊內(nèi)存的起始處,這個地址對我們來說極為重要,當(dāng)然不只是我們,是所有想要使用指針的程序員來說都很重要。

       該說的理論大概都說了,如果還有不懂還有懵懂的,可以看下我們的接口的具體實現(xiàn),如果發(fā)現(xiàn)確實還不懂的話可以直接提問,我想今天這是我們C語言的最后一講了,接下來我們該說說C++或是win32編程的知識了。

--------------------------------------

//my_list.c
#include
#include
#include 'my_list.h'

//全局函數(shù),把元素添加進(jìn)列表
static void CopyToNode(list *plist,iTem item)
{
         plist->item = item;
}

//初始化
void InitializeList(List *plist)
{
          *plist = NULL;
}
//確認(rèn)列表是否為空
bool ListIsEmpty(const List* plist)
{
            if(*plist == NULL)
                        return true;
            return false;
}
//確認(rèn)列表是否已滿
bool ListisFull(const List* plist)
{
            plist = (List*)malloc(sizeof(List));
            if(plist == NULL)
                      return true;
            else
                      return false;
}
//返回元素個數(shù)
unsigned int ListItemCount(const List *plist)
{
            unsigned int count = 0;
            list* pNode = *plist;
            while(pNode != NULL)
           {
                     count++;
                     pNode = pNode->next;
           }
           return count;
}
//添加元素,由于我們實現(xiàn)的是單向鏈表,所以使用從尾部添加
bool AddItem (iTem item, List* plist)
{
            list *pNew;
            list *pNode = *plist;
            pNew = (list*)malloc(sizeof(list));
            if(pNew == NULL)
                  return false;
            CopyToNode(pNew,item);
            pNew->next = NULL;
            if(pNode == NULL)
            {
                *plist = pNew;
            }
           else 
           {
  
                    while(pNode->next != NULL)
                    {
                             pNode  = pNode->next;
                     }
                    pNode->next = pNew;  
             }
             return true;
}

//顯示列表中的元素
void ShowListItem(const List *plist)
{
          list* pNode = *plist;
          while(pNode != NULL)
          {
                   printf('%-10s:%s\n',
                   pNode->item.Name,pNode->item.TelNumber);
                   pNode = pNode->next;
           }
}

//釋放內(nèi)存

void FreetheList (List *plist)

             list* pNode = *plist,*pSave;
             while(pNode != NULL)
             {
                   pSave = pNode->next;
                   free(pNode);
                   pNode = pSave;
             }
}

----------------------------------------------------

      接口的實現(xiàn)和我們有問題的實現(xiàn)看上去差不多,大家可以嘗試回頭去看看第二十八講里面的實現(xiàn),再來對比一下我們今天的實現(xiàn),看看問題出在那里,我們在二十八講里面是怎么瞞過大家雙眼的,如果大家能夠把這個地雷挖出來,C語言也算差不多了,至少在指針一塊有所理解了。
        下面我們還是用同一個驅(qū)動函數(shù)來測試:
----------------------------------------------
//ListTest.c
#include
#include
#include 'my_list.h'

int main()
{
         List BookPhone ;
         iTem TempPhone;
         unsigned int count;
        //使用初始化接口
         InitializeList(&BookPhone);
         if(!ListIsEmpty(&BookPhone))
                      exit(1);
         printf('請輸入聯(lián)系人的姓名:\n');
         while(gets_s(TempPhone.Name) != NULL &&TempPhone.Name[0] != '\0')
         {
                 printf('請輸入聯(lián)系人的電話號碼:\n');
                 gets_s(TempPhone.TelNumber);
                 if(ListisFull(&BookPhone))
                 {
                         printf('內(nèi)存已滿,請退出!');
                         exit(1);
                  }
                 //使用添加元素接口
                 if(!AddItem(TempPhone,&BookPhone))
                 {
                         printf('聯(lián)系人添加失?。。。?);
                         break;
                 }
                 while(getchar() != '\n')
                      continue;
                  printf('若還要儲存,請輸入下一個聯(lián)系人(空行退出):\n');
            }
            //接下來我們該顯示我們通信錄里面的聯(lián)系人信息了
            //使用判斷狀態(tài)接口
           if(ListIsEmpty(&BookPhone))
                      printf('通信錄沒有數(shù)據(jù)!?。?);
           else
           {
                printf('通信錄:\n');
                ShowListItem(&BookPhone);
           }
           //使用聯(lián)系人數(shù)量接口
           count = ListItemCount(&BookPhone);
           printf('聯(lián)系人數(shù)目:%d\n',count);
           //釋放內(nèi)存
           FreetheList(&BookPhone);
           return 0;
}

-------------------------------------------------

       現(xiàn)在我們來思考這次實踐的問題,在這次實踐中,我們雖然反轉(zhuǎn)糾結(jié)多次,但是我們卻沒有修改過驅(qū)動函數(shù)(也就是mian),而且接口我們也沒有修改,我們所動到的知識類型的聲明和接口的實現(xiàn),而我們的mian函數(shù)卻不關(guān)心我們的接口是怎么實現(xiàn)的,他只知道調(diào)用就好,至于怎么實現(xiàn)的與他無關(guān),我們可以任意修改,而不影響main的運行,現(xiàn)在是不是發(fā)現(xiàn)這種做法很有用呢?是啊,我們只要實現(xiàn)一類接口,以后當(dāng)我們想要實現(xiàn)這些功能的時候就無需在一步步的寫代碼,我們直接把我們的頭文件添加進(jìn)去就可以拉出就用,而且通常情況下我們這類接口是可以通用的,如果我們不想要電話薄,我們想要其他的,我們只需要把頭文件里面的iTem結(jié)構(gòu)修改成我們想要就好,而不會影響到我們接口,大家可以回頭去瞧瞧,除了顯示元素那個接口會受到影響外其他的毫無問題,當(dāng)然,為了通用型,我們可以重新寫個顯示元素的接口:

---------------------------------------

void ShowListiTem (const List * plist, void (* pfun) (iTem item));

----------------------------------------

      這個接口看起來有些復(fù)雜(對于初學(xué)者來說),不過不難理解,后面的void(*pfun)(iTem item)是一個函數(shù),以iTem類型為參數(shù)的函數(shù),*pfun是函數(shù)指針,也就是代表一個函數(shù)名,就好比,我們有個打印函數(shù)叫;

-----------------------------------

void Show(iTem item);

------------------------------------

        于是我們就可以這樣調(diào)用上面的接口:

--------------------------------------------------

ShowListiTem (&BookPhone, Show);

---------------------------------------------------

       所以,這個接口的意思就是讓一個函數(shù)作用于我們的List,于是這個接口就變得和類型無關(guān),變得通用。對于這個接口,我們可以這樣來實現(xiàn):

------------------------------------------------

void ShowListiTem(const List * plist, void (* pfun)(iTem item))
{
           list * pnode = *plist;  

           while (pnode != NULL)
           {
                   (* pfun) (pnode->item);  

                   pnode = pnode->next;     

           }
}

---------------------------------------------

      于是我們在我們的main函數(shù)后寫個Show函數(shù)來給這個接口使用:

--------------------------------------------

void  Show(iTem item)

{

        printf('%-10s:%s\n',
                   item.Name,item.TelNumber);

}

-------------------------------------------------

       這個Show函數(shù)可以根據(jù)我們需要的類型而修改,這樣一來,我們這個List就變得通用了。以后再要使用的時候就會很方便。

       最后,大家如果有興趣的話可以自己去嘗試一下vector,stack,二叉查找樹等等,這些我們就不打算說了,原理和我們的List差不多。當(dāng)然這些等到以后我們學(xué)C++的時候還會重點說,那時候你會發(fā)現(xiàn)這些算法模型的功能到底有多強(qiáng)大,C語言部分真的結(jié)束了,因為語法都和大家說了,怎么用就看大家的想法了,在編程里面我比較喜歡“想法”二字,尤其是電影一代宗師里面更是把這二字用得出神入化啊。

 

=====================

回復(fù)D(不區(qū)分大小寫)直接查看目錄

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    午夜视频成人在线免费| 日韩中文字幕有码午夜美女| 久久re6热在线视频| 国产三级视频不卡在线观看| 亚洲欧美日韩网友自拍| 综合久综合久综合久久| 日韩精品免费一区二区三区| 国产精品免费无遮挡不卡视频| 果冻传媒精选麻豆白晶晶 | 亚洲综合香蕉在线视频| 国产精品亚洲欧美一区麻豆| 午夜精品久久久免费视频| 日本不卡在线一区二区三区| 欧美亚洲三级视频在线观看| 成人午夜爽爽爽免费视频| 91蜜臀精品一区二区三区| 精品久久少妇激情视频| 欧洲日本亚洲一区二区| 国内午夜精品视频在线观看| 大香蕉久久精品一区二区字幕| 黄色国产一区二区三区| 亚洲av专区在线观看| 九九热在线免费在线观看| 欧美午夜色视频国产精品| 极品少妇嫩草视频在线观看| 成人综合网视频在线观看| 最近的中文字幕一区二区| 伊人色综合久久伊人婷婷| 草草视频精品在线观看| 国产在线观看不卡一区二区| 五月天婷亚洲天婷综合网| 国产水滴盗摄一区二区| 国产综合香蕉五月婷在线| 国产欧美日韩综合精品二区| 99久久人妻精品免费一区| 经典欧美熟女激情综合网| 久久亚洲精品成人国产| 99久久精品国产日本| 亚洲欧美黑人一区二区| 国产一区国产二区在线视频| 欧美不卡高清一区二区三区|