• 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]!=''){
    		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()!='
    ')
    		    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:
    ");
    	current=head;
    	while(current!=NULL){
    		printf("Movie:%s Rating:%d
    ",current->title,current->rating);
    		current=current->next;
    	} 
    	
    	//释放所分配的内存
    	current=head;
    	while(current!=NULL){
    		free(current);
    		current=current->next;
    	} 
    	printf("Bye!
    ");
    	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!
    ");
    		exit(1);
    	}
    
    /*收集并存储*/
        puts("Enter first movie title:");
        while(gets(temp.title)!=NULL && temp.title[0]!='')
        {
        	puts("Enter your rating<0-10>:");
        	scanf("%d",&temp.rating);
        	while(getchar()!='
    ')
        		continue;
        	if(AddItem(temp,&movies)==false)
        	{
        		fprintf(stderr,"Problem allocating memory
    ");
        		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:
    ");
        	Traverse(&movies,showmovies);
        }
        printf("You entered %d movies.
    ",ListItemCount(&movies));
    
    /*清除*/
        EmptyTheList(&movies);
        printf("Bye!
    ");
        return 0;
    }
    
    void showmovies(Item item)
    { 
    	printf("Movies:%s Rating:%d
    ",item.title,item.rating);
    } 
    

    注意:

    整个程序由三个文件组成,要运行这个程序,必须编译并链接film.c和list.c(关于编译多文件程序),工程建立如下:

    反思:

    使用ADT方法带来了什么?
    1.使用链表的方式暴露了所有编程的细节,而使用ADT方法则隐藏了这些细节,并用与任务直接相关的语言来表达程序。
    2.list.h和list.c文件共同组成可重用的资源。如果需要另一个简单列表,仍可用这些头文件。

    程序新知:

    1.“C预处理器和C库”中的#ifndef技术对多次包含一个文件提供保护
    2.“文件输入输出”fprintfgetc()函数等的使用
    3.编译多文件程序

  • 相关阅读:
    程序员必读书籍及部分图书导读指南
    Git客户端图文详解如何安装配置GitHub操作流程攻略
    c/c++中int main(int argc,char *argv[])的具体含义
    各大编程语言的特点/区别/类型系统
    Linux 下 make 命令 及 make 笔记
    STL之heap与优先级队列Priority Queue详解
    基金风险等级和客户风险承受能力评价标准公示
    markdown编辑器使用指南
    优秀程序员的良好的学习方式,特征,生活和学习的习惯
    db timedb / ClickHouse / clickhouse
  • 原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/5183409.html
Copyright © 2020-2023  润新知