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.编译多文件程序

posted @ 2016-02-05 19:04  zxzhang  阅读(2902)  评论(0编辑  收藏  举报