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

链表实现:

#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 dnsmasq 设置要点

    之前设置dnsmasq,一直没有奏效,后来摸索了一下,初步发现它的原理: 正常的流程应该是像这样的,先由client来发送DNS请求到网关,然后网关的dnsmasq处理这个请求, 再根据设置决定如何处 ...

  2. JavaScript的一些知识碎片(2)-反射-全局变量-回调

    JavaScript中的反射:编程语言中的反射原理都一样,就是通过操作metadata(描述语言的语言)来完成一些不具备反射功能的语言很难实现的功能.在静态语言中,反射是一个高大上的东西,比如在运行时 ...

  3. HoloLens开发手记 - 应用程序模型 App model

    HoloLens使用Universal Windows Platform (UWP)提供的应用模型.UWP应用模型定义了应用如何被安全和完全地安装.更新.版本控制和移除.它管理了应用生命周期 - 应用 ...

  4. 东大OJ-1430-PrimeNumbers

    题目描述 I'll give you a number , please tell me how many different prime factors in this number. 输入 The ...

  5. 100726A

    迭代深搜,从最深的地方搜,然后一个数被搜过了,标记用过,以后不再访问 #include<iostream> #include<cstring> #include<map& ...

  6. git初体验(四)git标签

    1.git 标题(tag) 关于git标签的描述,参考这里 2.标题管理 创建: 切换到需要打标签的分支上,如: git checkout master git tag v1.0 # 默认标签是打在最 ...

  7. selenium+webdriver+python 中警告框的处理方法

    在自动化测试过程中,经常会遇到弹出警告框的情况,如图所示: 在 WebDriver 中处理 JavaScript 所生成的 alert.confirm 以及 prompt 是很简单的.具体做法是使用  ...

  8. selenium3各种报错解决办法

    解决办法全在这个链接里 http://learn-automation.com/use-firefox-selenium-using-geckodriver-selenium-3

  9. Linux的vim三种模式及命令

    一般模式:在Linux终端中输入"vim 文件名"就进入了一般模式,但不能输入文字.编辑模式:在一般模式下按i就会进入编辑模式,此时就可以写程式,按Esc可回到一般模式. 命令模式 ...

  10. 【CCCC天梯赛决赛】

    cccc的天梯赛决赛,水题一样的水,中档题以上的还是没做出来.补了一下题,觉得其实也不是很难,主要是练的少. L2-1:红色预警 并查集 我做的时候想不到并查集,想到了也不一定做的出来,都是其实不难. ...