通常,程序开发最重要的部分是找到程序中表示数据的好方法:
- 选择数据类型
- 如何存储数据
- 设计管理数据的函数
算法:操控数据的方法
/*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);//List == Node *,那么 List * = Node * * plist,plist是指向指针的指针
//操作: 确定链表是否为空定义,plist指向一个已经初始化的链表
//后置条件: 如果链表为空,该函数返回true,否则返回false
bool ListIsEmpty(const List * plist);
//操作: 确定链表是否已满,plist指向一个已初始化的链表
//后置条件: 如果链表已满,该函数返回true,否则返回false
bool ListIsFull(const List *plist);
//操作: 确定链表中的项数
//后置条件: 该函数返回链表中的项数
unsigned int ListItemCount(const List * plist);
//操作: 在链表中添加项
//前提条件: item是一个待添加的项,plist是一个已初始化的链表
//后置条件: 如果添加成功返回true,否则返回false
bool AddItem(Item item, List * plist);
//操作: 把函数作用于链表中的每一项,pfun指向一个函数,该函数接受一个Item类型的参数,且无返回值
void Traverse(const List *plist, void(*pfun)(Item item));
//操作: 释放已分配的内存
//后置条件: 释放了为链表分配的所有内存,链表设置为空
void EmptyTheList(List * plist);
#endif
/*
list.h --- 定义数据结构提供用户接口的原型
lsit.c --- 提供函数代码实现接口
main.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)
{
//*plist指向的地址为空
if( *plist == NULL)
return true;
else
return false;
}
//如果链表已满,返回true
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 * current = *plist;
while(current != NULL)//从头指针开始判断,如果头指针为空,说明count==0
{
current = current->next;
count++;
}
return count;
}
//创建储存项的节点,并将其添加至由plist指向的链表末尾
bool AddItem(Item item, List * plist)
{
Node * pnew;
Node * scan = *plist;
pnew = (Node *)malloc(sizeof(Node));
if(pnew == NULL)
return false;//内存分配失败
pnew->item = item;
pnew->next = NULL;
if(scan == NULL)//头指针为空,说明是空链表
*plist = pnew; //直接把当前要添加的地址作为头指针
else
{
//只能一个一个往下走,找到最末尾的那个节点
while(scan->next != NULL)
{
scan = scan->next;
}
scan->next = pnew; //把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;
}
}
/*
处理多个链表是都把const List * list作为形参,表明这些函数不会更改链表,const防止了*plist(即plist所指向的量被修改),
在该程序中,plist指向movies,所以const防止这些函数修改movies。
所以不能出现:
*plist = (*plsit)->next; //如果*plist是const,不允许这样做
但是
(*plsit)->item.rating = 3; //即使*plist是const,也可以这样做
上面代码并未修改*plist,而是修改的*plist指向的数据
*/
#include <stdio.h>
#include <stdlib.h>
#include"list.h"
void showmovies(Item item);
char * s_gets(char * st, int n);
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 (s_gets(temp.title, TSIZE) != 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, "No memory available! Bye!\n");
break;
}
if (ListIsFull(&movies))
{
puts("The list is now full ");
break;
}
puts("Enter next movie title (empty line to stop):");
}
//显示
if(ListIsEmpty(&movies))
printf("No data entered.\n");
else
{
printf("Here is the movie list:\n");
Traverse(&movies, showmovies);
}
printf("You entered %d movies.\n", ListItemCount(&movies));
//清理
EmptyTheList(&movies);
puts("Bye!\n");
return 0;
}
void showmovies(Item item)
{
printf("Movie: %s Rating: %d\n", item.title,item.rating);
}
char * s_gets(char * st, int n)
{
char * ret_val;
char * find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
find = strchr(st, '\n');//查找换行符
if (find)//如果地址不是NULL
*find = '\0'; //在此书放置一个空字符
else
while (getchar() != '\n')
continue; //处理输入行中其他字符
}
return ret_val;
}