问题:写一个程序输入你一年看过的所有电影以及每部电影的各种信息(简化问题:每部电影只要求输入片名和评价)

链表实现:

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

C语言:链表实现的一个实例的更多相关文章

  1. C语言入门(17)——C语言数组应用的一个实例

    本篇通过一个实例介绍使用数组的一些基本模式.问题是这样的:首先生成一列0-9的随机数保存在数组中,然后统计其中每个数字出现的次数并打印,检查这些数字的随机性如何.随机数在某些场合(例如游戏程序)中是非 ...

  2. C语言链表实例--玩转链表

    下图为最一简单链表的示意图: 第 0 个结点称为头结点,它存放有第一个结点的首地址,它没有数据,只是一个指针变量.以下的每个结点都分为两个域,一个是数据域,存放各种实际的数据,如学号 num,姓名 n ...

  3. 一步一步教你从零开始写C语言链表---构建一个链表

      版权声明:本文为博主原创文章,如有需要,请注明转载地址:http://blog.csdn.net/morixinguan.若是侵权用于商业用途,请联系博主,否则将追究责任 https://blog ...

  4. 转 C# 只允许运行一个实例

    来源:http://blog.csdn.net/jin20000/article/details/3136791 互斥进程(程序), 简单点说,就是在系统中只能有该程序的一个实例运行. 现在很多软件都 ...

  5. 苹果公司的新的编程语言 Swift 高级语言(十一)--初始化类的析构函数的一个实例

    一 .实例的初始化          实例的初始化是准备一个类.结构或枚举的实例以便使用的过程. 初始化包含设置一个实例的每个存储属性为一个初始值,以及运行不论什么其他新的实例可以使用之前须要的设置或 ...

  6. C语言 链表

    原文:C语言 链表 最近在复习数据结构,想把数据结构里面涉及的都自己实现一下,完全是用C语言实现的. 自己编写的不是很好,大家可以参考,有错误希望帮忙指正,现在正处于编写阶段,一共将要实现19个功能. ...

  7. C语言库函数大全及应用实例十四

    原文:C语言库函数大全及应用实例十四                                       [编程资料]C语言库函数大全及应用实例十四 函数名: strset 功 能: 将一个串 ...

  8. C语言库函数大全及应用实例十三

    原文:C语言库函数大全及应用实例十三                                          [编程资料]C语言库函数大全及应用实例十三 函数名: stat 功 能: 读取打 ...

  9. C语言库函数大全及应用实例十

    原文:C语言库函数大全及应用实例十                                             [编程资料]C语言库函数大全及应用实例十 函数名: qsort 功 能: 使 ...

随机推荐

  1. Openwrt Image Builder/SDK 初探

    image builder和SDK既可以从官网上下载,又可以自己进行编译(make menuconfig).官网上下载的是预先帮你编译好的,这样可以大量节省自己编译源码花的时间,这两个东西相当于半成品 ...

  2. C# 序列化与反序列化

    我们先说说什么是序列化.所谓的序列化就是把要保存的内容以特定的格式存入某种介质中.比如我们要保存一些数据在下次程序启动的时候再读入.我们一般就是把这个数据写在一个本地的文本中.然后程序启动的时候去读入 ...

  3. 从语言到库到框架,再到API,再到标记最后到DSL语言

    计算机技术发展很快,而且越来越快,结果也是越来越复杂,那么我们到底怎么搞定复杂性并重用代码? 很明显,这是个大难题.一开始我们要解决计算问题,发展了基本的编程语言. 很快,编程语言不能满足需求,我们需 ...

  4. 构建Spark的Eclipse开发环境

    前言 无论Windows 或Linux 操作系统,构建Spark 开发环境的思路一致,基于Eclipse 或Idea,通过Java.Scala 或Python 语言进行开发.安装之前需要提前准备好JD ...

  5. 网络功能虚拟化(NFV)

    你造什么是网络功能虚拟化(NFV)吗? NFV将网络功能整合到行业标准的服务器.交换机和存储硬件上,提供了优化的虚拟化数据平面,NFV通过服务器上运行的软件让管理员取代传统物理网络设备,并降低成本.能 ...

  6. equals()的用法

    比如,两个对象 c1, c2; 那么,c1.equals(c2) == true; 则表示c1, c2两个变量的值是一致的 equals适用于所有对象,这是一种特殊方法 equals这种表现形式我们一 ...

  7. 77 swapon-激活Linux系统中交换空间

    Linux swapon命令用于激活Linux系统中交换空间,Linux系统的内存管理必须使用交换区来建立虚拟内存. 语法 /sbin/swapon -a [-v] /sbin/swapon [-v] ...

  8. oracle去重等基础问题

    --去重查询方法一:根据id select * from sxe where id in(select min(id) from sxe group by username) order by id ...

  9. oracle 在分区内查询数据

    查看当前分区 select t.partition_name,t.num_rows from all_tab_partitions t where table_name='table_name' 单个 ...

  10. PHP中CURL方法curl_setopt()函数的参数

    PHP CURL curl_setopt 参数 bool curl_setopt (int ch, string option, mixed value)curl_setopt()函数将为一个CURL ...