第17章 高级数据表示
17.1 研究数据表示
假设要编写一个程序,让用户输入一年内看过的电影,存储影片的信息。
可以使用结构储存电影,用结构数组存储多部电影。
但给数组分配空间时,会出现分配空间过大浪费或者分配空间过小不够用的问题。
使用动态内存(malloc)分配可以解决这个问题。
17.2 从数组到链表
struct file {
char title[TSIZE];
int rating;
struct film * next;
}
struct file *head;
链表看起来是一个特殊的结构,他包含自己的数据,还有下一个结构的地址. 另外有一个单独的head指针来标识开头地址。
用户输入时可以先给第一个结构分配内存空间,然后将他的next的地址赋值为NULL(空,因为现在他后面没有结构了)
在给第二个结构分配内存,然后将第一个结构的next指针指向第二个元素,将第二个元素的next设置为空......
具体用法见示例:
17.2.1 使用链表
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define TSIZE 45 //片名大小
struct film {
char title[TSIZE];
int rating;
struct film * next; //指向链表的下一个结构
};
char * s_gets(char * st, int n);
int main(void)
{
struct film * head = NULL;
struct film * prev = NULL, *current = NULL;
char input[TSIZE];
puts("输入第一部电影的名字:");
while (s_gets(input, TSIZE) != 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("输入评分<0-10>:");
scanf("%d", ¤t->rating);
while (getchar() != '\n')
continue;
puts("输入下一部电影名字(直接回车可退出)");
prev = current;
}
//显示电影
if (head == NULL)
printf("无数据.");
else
{
printf("电影列表如下:\n");
current = head;
while (current != NULL)
{
printf("电影:%s 评分:%d\n", current->title, current->rating);
current = current->next;
}
}
//释放内存
current = head;
while (head != NULL) //此处和书不同,书上运行出错。我认为这里应该判断head是否NULL而不是current是否为NULL
{
current = head;
head =head->next;
free(current);
}
printf("BYE\n");
return 0;
}
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)
*find = '\0'; //将换行符换成'\0'
else
while (getchar() != '\n') //处理输入行剩余的字符
continue;
}
return ret_val;
}
17.3 抽象数据类型(ADT,abstract data tye)
用更系统的方法定义数据类型:
类型特指两类信息:属性和操作。 //容易想到C++的类
17.3.1 建立抽象
17.3.2 建立接口
接口有两个部分:数据和操作
//下面是链表是具体实现
//list.h
#pragma once
#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指向一个已初始化的链表 */
/*后置条件: 如果链表为空,返回ture;否则返回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);
//list.cpp
#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;
}
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;
}
void Traverse(const List * plist, void(*pfun)(Item item))
{
Node * pnode = *plist;
while (pnode!= NULL)
{
(*pfun)(pnode->item);
pnode = pnode->next;
}
}
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;
}
film3.c
/*film3.c */
/*与list.c一起编译 */
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.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, "无可用内存,告辞。\n");
exit(1);
}
/*获取用户输入 并存储*/
puts("输入第一个电影名称:");
while (s_gets(temp.title, TSIZE) != NULL && temp.title[0] != '\0')
{
puts("输入你的评分<0-10>:");
scanf("%d", &temp.rating);
while (getchar() != '\n')
continue;
if (AddItem(temp, &movies) == false)
{
fprintf(stderr, "分配内存出错\n");
break;
}
if (ListIsFull(&movies))
{
puts("列表满了.");
break;
}
puts("输入下一步电影名称(回车结束程序)");
}
/*显示*/
if (ListIsEmpty(&movies))
printf("列表为空");
else
{
printf("Here is the movie list:\n");
Traverse(&movies, showMovies);
}
printf("你输入了%d个电影\n", ListItemCount(&movies));
/*清理*/
EmptyTheList(&movies);
printf("再见\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)
*find = '\0'; //将换行符换成'\0'
else
while (getchar() != '\n') //处理输入行剩余的字符
continue;
}
return ret_val;
}