C語言:鏈表實現的一個實例


問題:寫一個程序輸入你一年看過的所有電影以及每部電影的各種信息(簡化問題:每部電影只要求輸入片名和評價)

鏈表實現:

#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",&current->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.編譯多文件程序


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM