1. 程式人生 > >C語言ADT(抽象資料型別程式設計)

C語言ADT(抽象資料型別程式設計)

C語言是一種計算機程式設計語言。它既具有高階語言的特點,又具有組合語言的特點。它可以作為工作系統設計語言,編寫系統應用程式,也可以作為應用程式設計語言,編寫不依賴計算機硬體的應用程式。因此,它的應用範圍廣泛,不僅僅是在軟體開發上,而且各類科研都需要用到C語言,具體應用比如微控制器以及嵌入式系統開發。(摘自“百度百科”)

在嵌入式系統開發中,隨著系統功能要求越來越多,除了硬體系統不斷擴充套件外,晶片中軟體設計的規模也越來大,演算法越來越複雜,所以需要對程式結構進行良好設計,方便後來的修改和維護。

下面是對ADT的一些簡單介紹:

1.2.1 ADT定義及主要特點:

     為型別的屬性和可對型別執行的操作提供一個抽象的描述。不受特定的實現和程式語言的約束。這種正式的抽象描述被稱為抽象資料型別(Abstract Data Type,ADT)。

     抽象資料型別概念的引入,降低了大型軟體設計的複雜性;提高了系統的可讀性與可維護性;使系統的各部分相對隔離,在一定程式上解決了軟體的可靠性、生產率等方面的問題。    

1.2.2定義步驟:

   1、定義一個數據型別。提供儲存資料的方式,提供操作資料的方式。

   2、開發一個實現該ADT的程式設計介面。即說明如何儲存資料,並描述用於執行所需操作的函式集合。例如,提供一個結構體型別的定義,同時提供用來操作該結構體的函式的原型。

   3、編寫程式碼實現這個介面。

1.2.3抽象資料型別優點:

程式便於維護,靈活應對需求的變更;如果有些功能執行不正常,可以將問題集中到一個函式上;如果想用更好的辦法來完成一個任務,比如新增專案,則只需重新編寫那一個函式;如果需要增加新的屬性或操作,則修改抽象型別即可。        

上面的都是在本科階段學習C++裡面的一講《抽象資料型別》裡面的內容。當時教學時是先講C++基本的語法規則,再講類。C++基本的語法規則和C語言基本類似,所以在講C++“面向物件”最重要的特點――類之前,用“抽象資料型別”進行了一次過渡。C++目前基本上沒有用過,類也用得不深入,但是“抽象資料型別”的思想卻留下來了,這樣使得自己對大規模的C語言程式設計的基本模式有了比較好的基礎和概念了。

下面是結合ADT程式設計模式的例子進行介紹:


檢視工作區,裡面包含三個檔案:

list.h:資料的定義和宣告,介面函式的宣告

list.cpp:介面函式的實現

cw0901d.cpp:用於引用和測試程式碼塊

“list.h”檔案內容如下:

複製程式碼 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
    列表抽象資料型別的標頭檔案    
*/
#ifndef _LIST_H_
#define _LIST_H_

//////////////////////////////////////////////////////////////////////////////////特定於程式的宣告
//
const int TITLESIZE=51//存放片名的陣列長度
struct MyFilm
{
    char title[TITLESIZE];
    int rating;
};


//////////////////////////////////////////////////////////////////////////////////一般型別定義
//
typedef MyFilm Item;

struct Node
{
    Item item;
    Node *next;
};

typedef Node * List;

//////////////////////////////////////////////////////////////////////////////////函式原型
////操  作:初始化一個列表
//操作前:list引用一個列表
//操作後:該列表為初始化為空列表void InitializeList(List &list);

//操  作:確定列表是否為空列表
//操作前:list引用一個已初始化的列表
//操作後:如果該列表為空則返回true,否則返回falsebool ListIsEmpty(const List &list);

//操  作:確定列表是否已滿
//操作前:list引用一個已初始化的列表
//操作後:如果該列表已滿則返回true,否則返回falsebool ListIsFull(const List &list);

//操  作:確定列表中專案的個數
//操作前:list引用一個已初始化的列表
//操作後:返回列表中專案的個數unsigned int ListItemCount(const List &list);

//操  作:在列表的尾部新增一個專案
//操作前:item是要被新增到列表的專案
//        list引用一個已初始化的列表
//操作後:如果可能的話,在列表的尾部新增一個新專案,
//        成功則返回true,否則返回falsebool AppendItem(Item item, List &list);

//操  作:把一個函式作用於列表中的每個專案
//操作前:list引用一個已初始化的列表
//        pFun指向一個函式,該函式接受一個Item引用引數,
//        並且無返回值
//操作後:pFun指向的函式被作用到列表中的每一個專案一次void Traverse(const List &list, void (*pFun)(Item &item));

//操  作:釋放已分配的記憶體
//操作前:list引用一個已初始化的列表
//操作後:為該列表分配的記憶體已被釋放,
//        且該列表被置為空列表void EmptyList(List &list);

#endif 複製程式碼

 “list.cpp”檔案內容如下:

複製程式碼 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
    列表抽象資料型別的實現
*/
#include <iostream.h>
#include "list.h"

//把Item專案拷貝到列表的一個Node節點的item中static void CopyToNode(const Item &item, Node &node);

void InitializeList(List &list)
{
    list = NULL;
}

bool ListIsEmpty(const List &list)
{
    if (list==NULL)
        return true;
    else
        return false;
}

bool ListIsFull(const List &list)
{
    Node *pNode = new Node;

    if (pNode==NULL)
        return true;
    else
    {
        delete pNode;
        return false;
    }
}

unsigned int ListItemCount(const List &list)
{
    unsigned int count=0;
    Node *pNode = list;

    while (pNode != NULL)
    {
        count++;
        pNode = pNode->next;
    }

    return count;
}

bool AppendItem(Item item, List &list)
{
    Node *pNewNode, *pNode=list;

    pNewNode = new Node;
    if (pNewNode == NULL)
        return false;

    CopyToNode(item, *pNewNode);
    pNewNode->next = NULL;

    if (pNode == NULL)
        list = pNewNode;
    else
    {
        while (pNode->next != NULL)
            pNode = pNode->next;
        
        pNode->next = pNewNode;
    }

    return true;
}

void Traverse(const List &list, void (*pFun)(Item &item))
{
    Node *pNode = list;

    while (pNode != NULL)
    {
        pFun(pNode->item);
        pNode = pNode->next;
    }
}

void EmptyList(List &list)
{
    Node *pNode = list, *pTemp;

    while (pNode != NULL)
    {
        pTemp = pNode;
        pNode = pNode->next;

        delete pTemp;
    }
}

static void CopyToNode(const Item &item, Node &node)
{
    node.item = item; //結構體直接賦值實現拷貝} 複製程式碼

  “cw0901d.cpp”檔案內容如下: 

複製程式碼 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
    用定義的抽象資料型別——列表(List)——儲存電影列表
*/
#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>
#include "list.h"

void ShowFilm(Item &item); //顯示列表專案的函式
void main()
{
    List filmList;
    Item film;

    //初始化列表    InitializeList(filmList);
    if (ListIsFull(filmList))
    {
        cout<<"錯誤:記憶體不足!"<<endl;
        exit(1);
    }

    //讀取資料,建立列表    cout<<"請輸入第一個電影的片名:"<<endl;
    cin.getline(film.title, TITLESIZE);
    while (film.title[0]!='\0')
    {
        cout<<"請輸入你的評價等級(0~9):"<<endl;
        cin>>film.rating;
        cin.get();

        if (!AppendItem(film, filmList))
        {
            cout<<"錯誤:分配記憶體錯誤!"<<endl;
            break;
        }

        if (ListIsFull(filmList))
        {
            cout<<"錯誤:列表已滿!"<<endl;
            break;
        }

        cout<<"請輸入下一個電影的片名:"<<endl;
        cin.getline(film.title, TITLESIZE);
    }

    //輸出列表    if (ListIsEmpty(filmList))
    {
        cout<<"你沒有輸入有效的資料!"<<endl;
    }
    else
    {
        cout<<"下面是你輸入的電影列表:"<<endl;
        Traverse(filmList, ShowFilm);
    }

    cout<<"你一共輸入了"<<ListItemCount(filmList)<<"部電影。"<<endl;

    //清除    EmptyList(filmList);

    cout<<"\nBye!"<<endl;
}

void ShowFilm(Item &film)
{
    cout<<setw(TITLESIZE+1)<<setiosflags(ios::left)
        <<film.title<<film.rating<<endl;
} 複製程式碼

    在“list.h”中定義一種資料型別,這種資料型別一般是適用於你要解決的實際問題的一種結構體,並在此檔案中為這種抽象資料型別定義必要的方法,這些方法都在對應的“list.cpp”中進行實現,於是“list.h”和“list.cpp”就形成了一個功能模組,這就是用C語言的語法規則初步實現C++的物件“類”的思想。

實現模組化的引用的具體注意事項是:

1.       在list.h中定義抽象資料並宣告介面函式(將程式碼塊定義在一個條件編譯#ifndef……#define……#endif中可防止標頭檔案被重複包含)

2.       在list.cpp檔案的開頭包含list.h並實現介面函式)

3.       在測試檔案cw0901d.cpp檔案的開頭包含list.h標頭檔案(編譯器會自動根據標頭檔案去連線具體的.cpp具體程式碼實現的檔案)

實現上述的三個步驟後,cw0901d.cpp中就能直接對list.h中的資料和方法進行引用了,使用者不用再關心那些方法的具體實現,只需要知道這些函式訪求的功能即可。這樣做的好處不言而喻:一方面使得程式模組化,結構更加清晰,另外一方面使得程式的可擴充套件性的可修改性變強,更加方便維護。

後記:對於高階的面向物件的語言來說,本文肯定是多此一舉了,因為C++,C#的“類”完全比ADT要好用,但是對於C語言這種面向過程的語言來說,ADT無疑是將C++高階語言的程式設計思想在C語言中的一次很有意義的應用,使得大規模的嵌入式系統的C語言程式設計變得更加容易了。本文僅是簡單的整理和講解,具體過程其實可以直接套用附件模板。