《The C Programming Language》讀書筆記4

程序設計初步<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

   到現在爲止,我們已經對語言的基本元素有了個比較完整的瞭解了,但是總是停留在表達式等細節方面,我們很難寫出程序來,在今天任何一個程序都是個工程,如何組織我們已經掌握的這些基本元素,使得他們變成有一點功能的有機整體,這個就需要一個整體觀念的設計思想,對於c來說第一步該是過程化程序設計思想,換而言之,就是函數的設計,在上篇文字中我們已經看到了,其核心問題是如何分解要解決的問題,寫出各個有獨立功能的函數,然後由進入接口函數(在控制檯環境下,通常是main函數)組成完整的程序。但是光是這樣,我們能解決的問題相當有限,因爲在實際應用中,我們要處理的不是那麼簡單的內置類型(intchar等),而是比這些複雜的多的數據類型,因此第二步該是如何針對具體問題寫出抽象模型,即ADT(抽象數據類型),進而實現基於對象的設計思想,而學習指針和結構就需要帶着這樣的思想去探索,下面將通過一個簡單list(鏈表)的設計來簡要的說明一下該如何建立一個完整的程序。

  第一步,建立一個空項目,最好不要選擇“控制檯程序”模板,這樣能使得你的設計思路清楚明白,記住你現在在學習,方便快速不是你該追求的東西。

  第二步,靜下心來好好想一想。你的鏈表要提供那些接口、那些可以給用戶修改的部分(如具體的數據類型),這些放在用戶可見的list.h文件中。在本文中假設我們提供初始化、銷燬、增加節點、刪除節點、 插入節點、查找、和打印輸出幾項功能。那麼在上面的工程里加入一個叫llst.h的文件,輸入代碼如下:

#ifndef LIST_H

#define LIST_H

 

/*定義函數狀態*/

#ifndef ERR

#define ERR -1

#define OK 1

#endif

 

typedef int status;

typedef void type;     /*用戶可以根據具體需要更改此類型*/

 

typedef struct listitem {

    type           date;      /*節點數據*/

    struct listitem *next;    /*指向下個節點*/  

} list_node;//鏈表節點

typedef struct {

    struct listitem  *ptr;    /*鏈表頭指針*/

    int              size;    /*鏈表長度*/

} list;//鏈表

 

list* list_init ( void );     /*初始化*/

status list_destroy( list* ); /*銷燬*/

status add_node( list*, const type );  /*加入一個節點*/

status delete_all( list* );//清空

status delete_node( list*, list_node* ); /*刪除一個節點*/

status insert_node( list*, const type ); /*插入一個節點*/

list_node* find_node( const list*, const type ); /*查找*/

status list_print( const list* );    /*打印*/

#endif

第三步,在工程中加入list.c文件。Include了上面剛剛建立的頭文件,並實現每個極口,由於在通常情況下此文件並不是用戶可見(這裏把維護等問題除外),所以筆者沒加什麼註釋。當然這個不是什麼好習慣,這裏過於簡單,註釋就顯得有些多餘。

首先是include需要的頭文件:

#include "stdio.h"

#include "stdlib.h"

/*嚴格來說上面該用尖括號,由於網頁顯示不得已爲之*/

#include "list.h"

接下來是初始化和銷燬的實現

list* list_init ( void )

{

    list *p = ( list* )malloc( sizeof( list ) );

    if( p == 0 )

       return 0;

    p->ptr = 0;  

    p->size = 0;

    return p;

}

 

status list_destroy( list *pev )

{

    if( pev == 0 )

       return ERR;

    delete_all( pev );

    free( pev );

    return OK;

}

按理說,函數不能返回指針,呵呵,這裏有個很多初學者都誤會的問題,返回局部對象的左值和局部對象的引用(後者是c++中的說法)被返回的確不可以,因爲局部對象在函數的活動記錄(即函數調用棧中)分配,函數一旦結束局部對象被回收,返回的將是無效地址。因此象下面這樣的函數是錯誤的,

int* f()

{

      int *p, a;

      p = &a;

      return p;

}

但是由malloc分配的是堆上分配的,他不會隨着函數的結束而被回收。但是這樣用要相當小心,必須防止內存泄漏。程序結束前必須free掉該空間。

  下面就是完整的list.c

#include "stdio.h"

#include "stdlib.h"

/*嚴格來說此處處該用尖括號,由於網頁顯示不得已爲之*/

#include "list.h"

list* list_init ( void )

{

    list *p = ( list* )malloc( sizeof( list ) );

    if( p == 0 )

       return 0;

    p->ptr = 0;  

    p->size = 0;

    return p;

}

 

status list_destroy( list *pev )

{

    if( pev == 0 )

       return ERR;

    delete_all( pev );

    free( pev );

    return OK;

}

 

status add_node( list *p, const type date )

{

    list_node *pev =

       ( list_node* )malloc( sizeof( list_node ) );

    if( pev == 0 )

       return ERR;

    pev->date = date;

    pev->next = p->ptr;

    p->ptr = pev;

    p->size++;

    return OK;

}

 

status delete_node( list *p, list_node *pev )

{

    list_node *temp = pev;

    if( pev == 0 )

       return ERR;

    pev = temp->next;

    free( temp );

    p->size--;

    return OK;

}

 

status delete_all( list *pev )

{

    int ix;

    if( pev == 0 )

       return ERR;

    if( pev->size = 0 )

       return ERR;

    for( ix = 0; ix < pev->size; ++ix, ++pev->ptr )

       delete_node( pev, pev->ptr );

    return OK;

}

status insert_node( list *p, const type date )

{

    list_node *pev = p->ptr; ;

    if( p == 0 )

       return ERR;

    pev = find_node( p, date );

    if( pev == 0 )

    {

       type ia;

       printf( "輸入要插入的數/n" );

       scanf( "%d", &ia );

       add_node( p, ia );

    }

    else

    {

       type ia;

       list_node *pv =

           ( list_node* )malloc( sizeof( list_node ) );

       if( pev == 0 )

           return ERR;

       printf( "輸入要插入的數/n" );

       scanf( "%d", &ia );

       pv->date = ia;

       pv->next = pev->next;

       pev->next = pv;

       p->size++;

    }

    return OK;

}

 

list_node* find_node( const list *pev , const type date )

{

    int ix;

    list_node *p = pev->ptr;

    for( ix = 0; ix < pev->size; ++ix )

       if( p->date == date )

           return p;

       else

           p = p->next;

    return 0;

}

 

status list_print( const list *pev )

{

    int ix;

    list_node *p = pev->ptr;

    if( pev == 0 )

       return ERR;

    if( pev->size == 0 )

       return OK;

    for( ix = 0; ix < pev->size; ++ix )

    {

       printf( "%d/t", p->date );

       p = p->next;

    }

    printf( "/n" );

    return OK;

}

第四步,自己寫個main函數,由於個人的調試方式不同,這裏不給出代碼。只要確保每個函數都能正常工作就行了。

好了,到現在爲止我們把一個數據結構的實現走了一遍。當然,爲了簡單文字。筆者減少了很多list該有的功能。很多人認爲我寫太爛,現在再次說明,本文和初學者交流的文字,高手們就不必在這篇文字浪費你的時間了

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章