C Primer Plus之高级数据表示
抽象数据类型(ADT)
类型是由什么组成?一个类型(type)指定两类信息:一个属性集和一个操作集。
所以您想定义一个新的数据类型。首先,您需要提供存储数据的方式,可能是通过设计一个结构。第二个,需要提供操作数据的方式。
计算机科学已经研究出一种定义新类型的成功方法。这种方法使用3个步骤来完成从抽象到具体的过程:
- 为类型的属性和可对类型执行的操作提供一个抽象的描述。这个描述不应受任何特定实现的约束,甚至不应受到任何特定编程语言的约束。这样一种正式的抽象描述被称为抽象数据类型(ADT)
- 开发一个实现该ADT的编程接口(即函数集合)。即说明如何存储数据并描述用于执行所需操作的函数集合。比如,在C中,您可能同时提供一个结构的定义和用来操作该结构 的函数的原型。
- 编写代码来实现这个接口。这一步至关重要,但是使用这种新类型的程序员无须了解实现的细节。
以抽象数据类型——列表为例进行说明
类型名称: | 列表 |
类型属性: | 可保存一个项目序列 |
类型操作: | 把列表初始化为空列表 |
确定列表是否为空 | |
确定列表是否已满 | |
确定列表中项目的个数 | |
向列表末尾添加项目 | |
遍历列表,处理列表中每个项目 | |
清空列表 | |
在列表中的任何位置插入一个项目 | |
从列表中删除一个项目 | |
取出列表中的一个项目(不改变列表) | |
替换列表中的一个项目 | |
在列表中搜索一个项目 |
非正式但抽象的列表定义时:它是一个能够保存项目序列并且可以对其应用任何前面的操作的数据对象。这个定义没有说明什么样的项目才能存储在列表中。它并未指定是否应该使用数组或链接的结构集或其他数据形式来保存这些项目。它并未指定使用何种方法来实现诸如获取列表中的元素个数之类的操作。这些都是留给实现的细节。
构造C语言接口
简单列表的接口有两个部分:①描述数据如何表示,②描述实现ADT操作的函数。接口的设计应和ADT的描述尽可能密切地保持一致。因此,应该使用某种通用的Item类型来进行表达,而不是用诸如int或struct film之类的专用类型。这样做的方法之一是使用C的typedef工具将Item定义为所需类型。如:
#define TSIZE 45 /* 存放片名的数组大小 */
struct film {
char title[TSIZE];
int rating;
};
typedef struct film Item;
在链表的实现中,每一个链接被称为一个节点(node)。每一个节点包含形成列表内容的信息和指向下一节点的指针。例如:
typedef struct node {
Item item;
struct node * next; // 指向下一节点的指针
} Node;
typedef Node * List;
↓ 等价于
struct node {
Item item;
struct node * next;
};
typedef struct node Node;
typedef Node * List; // 为了管理链表,需要一个指向其开始处的指针
还可以加入一个变量来保存列表中项目的数量:
typedef struct list {
Node * head; // 指向列表头的指针
int size; // 列表中项目的数量
} List;
考虑如下声明:
List movies;
movies是在建立一个列表,而不是在建立一个指向节点的指针或是建立一个结构。movies的确切数据表示是应该在接口层上不可见的实现细节上。
启动后,程序应该把头指针初始化为NULL。任何使用List类型的人都应无须担心细节,而应能够使用下面的代码:
InitializeList(movies);
程序员只需要知道他们应该使用InitializeList()函数来初始化列表,不需要知道List变量的确切的数据实现。这是数据隐藏的一个例子。数据隐藏是一种对跟高级编程隐藏数据表示细节的艺术。InitializeList()的函数原型为:
/* 操作: 初始化一个列表 */
/* 操作前:plist指向一个列表 */
/* 操作后:该列表被初始化为空列表 */
void InitializeList(List * plist);
C语言把所有的类型和函数信息集成到一个包中的方法是:将类型定义和函数原型(包括“操作前”和“操作后”注释)放入一个头文件中。这个文件将提供程序员使用该类型所需的全部信息。
单链表的实现与分析(以C Primer Plus(第5版) 第17章 高级数据表示——程序清单17.4为例)
抽象数据类型的头文件(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; // 指向Node(struct node)类型的指针 /* 函数原型 */
/* 操作:初始化一个列表 */
/* 操作前:plist指向一个列表 */
/* 操作后:该列表被初始化为空列表 */
void InitializeList(List * plist); /* 操作:确定列表是否为空列表 */
/* 操作前:plist指向一个已初始化的列表 */
/* 操作后:如果该列表为空则返回true:否则返回true */
bool ListIsEmpty(const List plist); /* 操作:确定列表是否已满 */
/* 操作前:plist指向一个已初始化的列表 */
/* 操作后:如果该列表已满则返回true:否则返回true */
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 // LIST_H_
提供函数代码以实现接口
#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 = ;
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->item = item;
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;
}
将列表接口应用于具体编程问题的源代码文件
#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();
}
// 收集并存储
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() != '\n')
continue;
// 在列表尾部添加一个项目
if(AddItem(temp, &movies) == false)
{
fprintf(stderr, "Problem allocating memory\n");
break;
}
// 每添加一个项目,确定列表是否已满
if(ListIsFull(&movies))
{
puts("The list is now full.");
break;
}
puts("Enter next movies 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 ;
}
void showmovies(Item item)
{
printf("Movie: %s Rating: %d\n", item.title, item.rating);
}
C Primer Plus之高级数据表示的更多相关文章
- C语言学习书籍推荐《C Primer Plus(中文版)(第5版)》下载
普拉塔 (Prata S.) (作者), 云巅工作室 (译者) <C Primer Plus(中文版)(第5版)>共17章,介绍了C语言的基础知识,包括数据类型.格式化输入输出.运算符.表 ...
- 《C Primer Plus(第6版)(中文版)》普拉达(作者)epub+mobi+azw3
内容简介 <C Primer Plus(第6版)中文版>详细讲解了C语言的基本概念和编程技巧. <C Primer Plus(第6版)中文版>共17章.第1.2章介绍了C语言编 ...
- 关于C primer plus 的学习计划(暂停)
最近想学数据结构,但是C的基础不够好,想借着C primer plus补一下基础.怎料第一章看的还挺快,到第二章看了二十多天.现在改改阅读方式:每日在这里添加进度,然后精看例题习题和章总结,其它简略看 ...
- C Primer Plus(三)
重读C Primer Plus ,查漏补缺 重读C Primer Plus,记录遗漏的.未掌握的.不清楚的知识点 文件输入/输出 1.fgets函数在读取文件内容时会将换行符读入,但gets不会,fp ...
- C Primer Plus(第五版)12
第 12 章 存储类, 链接和内存管理 在本章中你将学习下列内容 . 关键字: auto, extern, static, register, const, volatile, restricted. ...
- 学长们的求职血泪史(C/C++/JAVA)
以下分三个方向讲解,每个方向都是一个学长独自撰稿. (一) C语言篇 C语言求职血泪史 华为(实习):机试.一面.性格测试被鄙视.优招被鄙视.普招被鄙视 锐捷:笔试.面试莫名其妙被鄙视 创新工场:笔 ...
- 论文翻译:2018_Artificial Bandwidth Extension with Memory Inclusion using Semi-supervised Stacked Auto-encoders
论文地址:使用半监督堆栈式自动编码器实现包含记忆的人工带宽扩展 作者:Pramod Bachhav, Massimiliano Todisco and Nicholas Evans 博客作者:凌逆战 ...
- C++ Primer(第4版)-学习笔记-第5部分:高级主题
第17章 用于大型程序的工具 异常处理 不存在数组或函数类型的异常.相反,如果抛出一个数组,被抛出的对象转换为指向数组首元素的指针,类似地,如果抛出一个函数,函数被转换为指向该函数的指针. 不要抛出 ...
- [翻译]Primer on Cognitive Computing(认知计算入门)
Source Kelly J., Primer on Cognitive Computing 20150216. 侵删,联系方式:zhoujiagen\@gmail.com. 按A candidate ...
随机推荐
- Window Phone 8开发问题反思
项目开发有段时间了,进入了阶段测试.然而在测试过程中bug连连不断,在抱怨产品需求的坑爹.不合理之外,我也一直在反思为什么会出现这么多Bug. 首先,由于项目开发的两个人都是新手,在刚刚认识MVVM架 ...
- 修改ubuntu按电源键触发效果
GUI内终端执行如下指令,或者加入开机启动脚本内 gsettings set org.gnome.settings-daemon.plugins.power button-power shutdown ...
- ADO.NET笔记——利用Command对象的ExecuteScalar()方法返回一个数据值
相关知识: 有些SQL操作,例如SUM,只会从数据库返回一个数据值,而不是多行数据 尽管也可以使用ExecuteReader()返回一个DataReader对象,代表该数据值,但是使用Command对 ...
- 9种jQuery和css3图片动画特效代码演示
1.自由旋转的jQuery图片 演示和下载地址 2.css3阴影动画效果 演示和下载地址 3.拉窗帘特效图片 演示和下载地址 4.css3文字特效动画 演示和下载地址 5.css3时钟代码 演示和下载 ...
- 如何使用NET Reactor为您的.Net(C#,VB.Net) 源代码加密
前言 VS开发的源代码安全性,是很多开发者头痛的事情.于是保护好源代码便成了开发者们最关心的事情之一了. 在网上搜一搜,很多有不少的第三方工具可以为源代码加密.加密方式不外乎就是混淆,加壳. 理论上, ...
- centOS 一键php环境安装-php博弈
我是方少,闲着没事,感觉每次编译安装,再修改php,mysql,redis,nginx配置文件觉得把大好的青春时间都浪费掉了.如是想着,怎样一键安装 php环境和相关配置.于是拜读了一下lnmp的一键 ...
- Laravel 5 基础(二)- 路由、控制器和视图简介
查看 app/Http/routes.php Route::get('/', 'WelcomeController@index'); @是一个界定符,前面是控制器,后面是动作,表示当用户请求url / ...
- uploadify 上传
本来想做一套上传公用的组建的,里面包含文件转码等功能,看来这些都只能后来一步一步加上了,先写下来... 1,引入脚本等 @{ Layout = null; } <!DOCTYPE html> ...
- python初试牛刀
需求:在L7的一台机器上做nginx配置,然后代码分发到别的所有的机器上.由于目录中有很多配置文件,而且防止误操作,需要修改配置之前先备份原配置.然后需要在运行修改配置的脚本之前,先弹出界面,告知操作 ...
- Configure Database Mirroring
使用证书配置的镜像基本安装微软次序做就可以了 http://msdn.microsoft.com/zh-cn/library/ms191140.aspx 备份还原首先要转换成完全备份模式没什么好多说的 ...