問題:寫一個程序輸入你一年看過的所有電影以及每部電影的各種信息(簡化問題:每部電影只要求輸入片名和評價)
鏈表實現:
#include<stdio.h>
#include<stdlib.h>//提供malloc()原型
#include<string.h>
#define TSIZE 45
struct film{
char title[TSIZE];
int rating;
struct film * next;
};
int main()
{
struct film * head = NULL;
struct film * prev, *current;
char input[TSIZE];
//收集並儲存信息
puts("Enter first movie title:");
while(gets(input)!=NULL&&input[0]!='\0'){
current = (struct film *)malloc(sizeof(struct film));
if(head==NULL)
head=current;
else
prev->next=current;
current->next=NULL;
strcpy(current->title, input);
puts("Enter your rating <0-10>:");
scanf("%d",¤t->rating);
while(getchar()!='\n')
continue;
puts("Enter next movie title(empty line to stop):");
prev=current;
}
//給出電影列表
if(head==NULL)
printf("No data entered.");
else
printf("Here is the movie list:\n");
current=head;
while(current!=NULL){
printf("Movie:%s Rating:%d\n",current->title,current->rating);
current=current->next;
}
//釋放所分配的內存
current=head;
while(current!=NULL){
free(current);
current=current->next;
}
printf("Bye!\n");
return 0;
}
程序分析:
不使用head遍歷整個列表而使用一個新指針current是因為head會改變head的值,這樣程序將不再能找到列表的開始處。
由malloc()分配的內存在程序終止時雖然會自動清理,但仍要記得調用free()來釋放malloc()分配的內存,養成良好的習慣。
反思:
程序沒有檢查malloc()是否找到需要的內存,並且沒有提供刪除列表中項目的功能。
抽象數據類型實現:
1.構造接口
list.h接口文件
#ifndef LIST_H_
#define LIST_H_
#include<stdbool.h>
/*特定程序的聲明*/
#define TSIZE 45
struct film
{
char title[TSIZE];
int rating;
};
/*一般類型定義*/
typedef struct film Item;
typedef struct node
{
Item item;
struct node * next;
}Node;
typedef Node * List;
/*函數原型*/
/*操作:初始化一個列表*/
/*操作前:plist指向一個列表*/
/*操作后:該列表被初始化為空列表*/
void InitializeList(List * plist);
/*操作:確定列表是否為空列表*/
/*操作前:plist指向一個已初始化的列表*/
/*操作后:如果該列表為空則返回true;否則返回false*/
bool ListIsEmpty(const List * plist);
/*操作:確定列表是否已滿*/
/*操作前:plist指向一個已初始化的列表*/
/*操作后:如果該列表為空則返回true;否則返回false*/
bool ListIsFull(const List * plist);
/*操作:確定列表中項目的個數*/
/*操作前:plist指向一個已初始化的列表*/
/*操作后:返回該列表中項目的個數*/
unsigned int ListItemCount(const List * plist);
/*操作:在列表尾部添加一個項目*/
/*操作前:item是要被增加到列表的項目 plist指向一個已初始化的列表*/
/*操作后:如果可能的話,在列表尾部添加一個新項目,函數返回true,否則函數返回false*/
bool AddItem(Item item,List * plist);
/*操作:把一個函數作用於列表中的每個項目*/
/*操作前:plist指向一個已初始化的列表 pfun指向一個函數,該函數接受一個Item參數並且無返回值*/
/*操作后:pfun指向的函數被作用到列表中的每個項目一次*/
void Traverse(const List * plist,void(* pfun)(Item item));
/*操作:釋放已分配的內存(如果有)*/
/*操作前:plist指向一個已初始化的列表*/
/*操作后:為該列表分配的內存已被釋放並且該列表被置為空列表*/
void EmptyTheList(List * plist);
#endif
2.實現接口:
list.c文件
#include<stdio.h>
#include<stdlib.h>
#include "list.h"
/*局部函數原型*/
static void CopyToNode(Item item,Node * pnode);
/*接口函數*/
/*把列表設置為空列表*/
void InitializeList(List * plist)
{
* plist = NULL;
}
/*如果列表為空則返回真*/
bool ListIsEmpty(const List * plist)
{
if(* plist == NULL)
return true;
else
return false;
}
/*如果列表已滿則返回真*/
bool ListIsFull(const List * plist)
{
Node * pt;
bool full;
pt = (Node *)malloc(sizeof(Node));
if(pt==NULL)
full=true;
else
full=false;
free(pt);
return full;
}
/*返回節點數*/
unsigned int ListItemCount(const List * plist)
{
unsigned int count = 0;
Node * pnode = *plist;
while(pnode!=NULL)
{
++count;
pnode=pnode->next;
}
return count;
}
/*創建存放項目的節點,並把它添加到由plist指向的列表(較慢的實現方法)尾部*/
bool AddItem(Item item,List * plist)
{
Node * pnew;
Node * scan = *plist;
pnew = (Node *)malloc(sizeof(Node));
if(pnew==NULL)
return false;
CopyToNode(item,pnew);
pnew->next=NULL;
if(scan==NULL)
*plist=pnew;
else
{
while(scan->next!=NULL)
scan=scan->next;
scan->next=pnew;
}
return true;
}
/*訪問每個節點並對它們分別執行由pfun指向的函數*/
void Traverse(const List * plist,void(*pfun)(Item item))
{
Node *pnode=*plist;
while(pnode!=NULL)
{
(*pfun)(pnode->item);
pnode=pnode->next;
}
}
/*釋放由malloc()分配的內存*/
/*把列表指針設置為NULL*/
void EmptyTheList(List * plist)
{
Node * psave;
while(*plist!=NULL)
{
psave=(*plist)->next;
free(*plist);
*plist=psave;
}
}
/*局部函數定義*/
/*把一個項目復制到一個節點中*/
static void CopyToNode(Item item,Node *pnode)
{
pnode->item=item;
}
3.使用接口:
film.c
#include<stdio.h>
#include<stdlib.h>
#include "list.h"
void showmovies(Item item);
int main(void)
{
List movies;
Item temp;
/*初始化*/
InitializeList(&movies);
if(ListIsFull(movies))
{
fprintf(stderr, "No memory available!Bye!\n");
exit(1);
}
/*收集並存儲*/
puts("Enter first movie title:");
while(gets(temp.title)!=NULL && temp.title[0]!='\0')
{
puts("Enter your rating<0-10>:");
scanf("%d",&temp.rating);
while(getchar()!='\n')
continue;
if(AddItem(temp,&movies)==false)
{
fprintf(stderr,"Problem allocating memory\n");
break;
}
if(ListIsFull(movies))
{
puts("The list if now full.");
break;
}
puts("Enter next movie title(empty line to stop):");
}
/*顯示*/
if(ListIsEmpty(movies))
printf("No data entered.");
else
{
printf("Here is the movie list:\n");
Traverse(&movies,showmovies);
}
printf("You entered %d movies.\n",ListItemCount(&movies));
/*清除*/
EmptyTheList(&movies);
printf("Bye!\n");
return 0;
}
void showmovies(Item item)
{
printf("Movies:%s Rating:%d\n",item.title,item.rating);
}
注意:
整個程序由三個文件組成,要運行這個程序,必須編譯並鏈接film.c和list.c(關於編譯多文件程序),工程建立如下:
反思:
使用ADT方法帶來了什么?
1.使用鏈表的方式暴露了所有編程的細節,而使用ADT方法則隱藏了這些細節,並用與任務直接相關的語言來表達程序。
2.list.h和list.c文件共同組成可重用的資源。如果需要另一個簡單列表,仍可用這些頭文件。
程序新知:
1.“C預處理器和C庫”中的#ifndef技術對多次包含一個文件提供保護
2.“文件輸入輸出”fprintfgetc()函數等的使用
3.編譯多文件程序