程序設計初步<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
到現在爲止,我們已經對語言的基本元素有了個比較完整的瞭解了,但是總是停留在表達式等細節方面,我們很難寫出程序來,在今天任何一個程序都是個工程,如何組織我們已經掌握的這些基本元素,使得他們變成有一點功能的有機整體,這個就需要一個整體觀念的設計思想,對於c來說第一步該是過程化程序設計思想,換而言之,就是函數的設計,在上篇文字中我們已經看到了,其核心問題是如何分解要解決的問題,寫出各個有獨立功能的函數,然後由進入接口函數(在控制檯環境下,通常是main函數)組成完整的程序。但是光是這樣,我們能解決的問題相當有限,因爲在實際應用中,我們要處理的不是那麼簡單的內置類型(int,char等),而是比這些複雜的多的數據類型,因此第二步該是如何針對具體問題寫出抽象模型,即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該有的功能。很多人認爲我寫太爛,現在再次說明,本文和初學者交流的文字,高手們就不必在這篇文字浪費你的時間了